Skip to content

Commit 94bb2b6

Browse files
authored
Merge pull request #606 from fortran-lang/feat/improve-logs
Improves extension logger
2 parents 755ce0c + ce26f1e commit 94bb2b6

14 files changed

+217
-177
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added option to set the verbosity of the Output Channel
13+
([#606](https://github.com/fortran-lang/vscode-fortran-support/pull/606))
14+
- Added increased logging messages in various parts of the extension
15+
([#606](https://github.com/fortran-lang/vscode-fortran-support/pull/606))
16+
1017
### Changed
1118

19+
- Changed the way messages are logged and added `log` syntax highlighting
20+
([#606](https://github.com/fortran-lang/vscode-fortran-support/pull/606))
1221
- Removes duplicate diagnostic messages from the linter
1322
([#598](https://github.com/fortran-lang/vscode-fortran-support/issues/598))
1423

package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@
180180
],
181181
"markdownDescription": "Specify the word case to use when suggesting autocomplete options.",
182182
"order": 60
183+
},
184+
"fortran.logging.level": {
185+
"type": "string",
186+
"default": "Info",
187+
"enum": [
188+
"None",
189+
"Error",
190+
"Warn",
191+
"Info",
192+
"Debug"
193+
],
194+
"markdownDescription": "The log level for the extension.",
195+
"order": 70
183196
}
184197
}
185198
},
@@ -560,5 +573,11 @@
560573
"glob": "^8.0.3",
561574
"vscode-languageclient": "^8.0.2",
562575
"which": "^2.0.2"
576+
},
577+
"__metadata": {
578+
"id": "64379b4d-40cd-415a-8643-b07572d4a243",
579+
"publisherDisplayName": "The Fortran Programming Language",
580+
"publisherId": "0fb8288f-2952-4d83-8d25-46814faecc34",
581+
"isPreReleaseVersion": false
563582
}
564583
}

src/extension.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import { FortlsClient } from './lsp/client';
1212
import { FortranHoverProvider } from './features/hover-provider';
1313
import { FortranLintingProvider } from './features/linter-provider';
1414
import { EXTENSION_ID, FortranDocumentSelector } from './lib/tools';
15-
import { LoggingService } from './services/logging-service';
15+
import { getConfigLogLevel, Logger } from './services/logging';
1616
import { WhatsNew } from './features/commands';
1717

1818
// Make it global to catch errors when activation fails
19-
const loggingService = new LoggingService();
19+
const logger = new Logger(
20+
vscode.window.createOutputChannel('Modern Fortran', 'log'),
21+
getConfigLogLevel()
22+
);
2023

2124
export async function activate(context: vscode.ExtensionContext) {
2225
const config = vscode.workspace.getConfiguration(EXTENSION_ID);
@@ -27,34 +30,42 @@ export async function activate(context: vscode.ExtensionContext) {
2730
const symbolsType = config.get<string>('provide.symbols');
2831
detectDeprecatedOptions();
2932

30-
loggingService.logInfo(`Extension Name: ${pkg.displayName}`);
31-
loggingService.logInfo(`Extension Version: ${pkg.version}`);
32-
loggingService.logInfo(`Linter set to: ${linterType}`);
33-
loggingService.logInfo(`Formatter set to: ${formatterType}`);
34-
loggingService.logInfo(`Autocomplete set to: ${autocompleteType}`);
35-
loggingService.logInfo(`Hover set to: ${hoverType}`);
36-
loggingService.logInfo(`Symbols set to: ${symbolsType}`);
33+
logger.info(`Extension Name: ${pkg.displayName}`);
34+
logger.info(`Extension Version: ${pkg.version}`);
35+
logger.info(`Linter set to: "${linterType}"`);
36+
logger.info(`Formatter set to: "${formatterType}"`);
37+
logger.info(`Autocomplete set to: "${autocompleteType}"`);
38+
logger.info(`Hover set to: "${hoverType}"`);
39+
logger.info(`Symbols set to: "${symbolsType}"`);
3740

41+
context.subscriptions.push(
42+
vscode.workspace.onDidChangeConfiguration(e => {
43+
if (e.affectsConfiguration(`${EXTENSION_ID}.logging.level`)) {
44+
// Leave config field empty to fetch the most updated config values
45+
logger.setLogLevel(getConfigLogLevel());
46+
}
47+
})
48+
);
3849
// Linter is always activated but will only lint if compiler !== Disabled
39-
const linter = new FortranLintingProvider(loggingService);
50+
const linter = new FortranLintingProvider(logger);
4051
linter.activate(context.subscriptions);
4152
vscode.languages.registerCodeActionsProvider(FortranDocumentSelector(), linter);
4253

4354
if (formatterType !== 'Disabled') {
4455
const disposable: vscode.Disposable = vscode.languages.registerDocumentFormattingEditProvider(
4556
FortranDocumentSelector(),
46-
new FortranFormattingProvider(loggingService)
57+
new FortranFormattingProvider(logger)
4758
);
4859
context.subscriptions.push(disposable);
4960
}
5061

5162
if (autocompleteType === 'Built-in') {
52-
const completionProvider = new FortranCompletionProvider(loggingService);
63+
const completionProvider = new FortranCompletionProvider(logger);
5364
vscode.languages.registerCompletionItemProvider(FortranDocumentSelector(), completionProvider);
5465
}
5566

5667
if (hoverType === 'Built-in' || hoverType === 'Both') {
57-
const hoverProvider = new FortranHoverProvider(loggingService);
68+
const hoverProvider = new FortranHoverProvider(logger);
5869
vscode.languages.registerHoverProvider(FortranDocumentSelector(), hoverProvider);
5970
}
6071

@@ -64,7 +75,7 @@ export async function activate(context: vscode.ExtensionContext) {
6475
}
6576

6677
if (!config.get<boolean>('fortls.disabled')) {
67-
new FortlsClient(loggingService, context).activate();
78+
new FortlsClient(logger, context).activate();
6879
}
6980
// override VS Code's default implementation of the debug hover
7081
// here we match Fortran derived types and scope them appropriately
@@ -129,7 +140,7 @@ function detectDeprecatedOptions() {
129140
if (selected === 'Open Settings') {
130141
vscode.commands.executeCommand('workbench.action.openGlobalSettings');
131142
}
132-
loggingService.logError(`The following deprecated options have been detected:\n${oldArgs}`);
143+
logger.error(`The following deprecated options have been detected:\n${oldArgs}`);
133144
});
134145
}
135146

src/features/completion-provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as vscode from 'vscode';
44
import { isPositionInString, FORTRAN_KEYWORDS } from '../lib/helper';
55
import { getDeclaredFunctions } from '../lib/functions';
66
import { EXTENSION_ID } from '../lib/tools';
7-
import { LoggingService } from '../services/logging-service';
7+
import { Logger } from '../services/logging';
88
import intrinsics from './intrinsics.json';
99

1010
class CaseCoverter {
@@ -35,7 +35,7 @@ class CaseCoverter {
3535
}
3636

3737
export class FortranCompletionProvider implements vscode.CompletionItemProvider {
38-
constructor(private loggingService: LoggingService) {}
38+
constructor(private logger: Logger) {}
3939
public provideCompletionItems(
4040
document: vscode.TextDocument,
4141
position: vscode.Position,

src/features/formatting-provider.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as which from 'which';
66
import * as vscode from 'vscode';
77
import * as cp from 'child_process';
88

9-
import { LoggingService } from '../services/logging-service';
9+
import { Logger } from '../services/logging';
1010
import {
1111
FORMATTERS,
1212
EXTENSION_ID,
@@ -18,7 +18,7 @@ import {
1818
export class FortranFormattingProvider implements vscode.DocumentFormattingEditProvider {
1919
private readonly workspace = vscode.workspace.getConfiguration(EXTENSION_ID);
2020
private formatter: string | undefined;
21-
constructor(private logger: LoggingService) {}
21+
constructor(private logger: Logger) {}
2222

2323
public async provideDocumentFormattingEdits(
2424
document: vscode.TextDocument,
@@ -32,7 +32,7 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
3232
} else if (formatterName === 'findent') {
3333
return this.doFormatFindent(document);
3434
} else {
35-
this.logger.logError('Cannot format document with formatter set to Disabled');
35+
this.logger.error('[format] Cannot format document with formatter set to Disabled');
3636
}
3737

3838
return undefined;
@@ -46,7 +46,7 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
4646
private async doFormatFprettify(document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
4747
// fprettify can only do FortranFreeFrom
4848
if (document.languageId !== 'FortranFreeForm') {
49-
this.logger.logError(`fprettify can only format FortranFreeForm, change
49+
this.logger.error(`[format] fprettify can only format FortranFreeForm, change
5050
to findent for FortranFixedForm formatting`);
5151
return undefined;
5252
}
@@ -56,17 +56,17 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
5656
const formatter: string = path.join(formatterPath, formatterName);
5757
// If no formatter is detected try and install it
5858
if (!which.sync(formatter, { nothrow: true })) {
59-
this.logger.logWarning(`Formatter: ${formatterName} not detected in your system.
60-
Attempting to install now.`);
59+
this.logger.warn(`[format] ${formatterName} not found. Attempting to install now.`);
6160
const msg = `Installing ${formatterName} through pip with --user option`;
6261
promptForMissingTool(formatterName, msg, 'Python', ['Install'], this.logger);
6362
}
6463

6564
const args: string[] = ['--stdout', ...this.getFormatterArgs()];
65+
this.logger.debug(`[format] fprettify args:`, args);
6666
const edits: vscode.TextEdit[] = [];
6767
const [stdout, stderr] = await spawnAsPromise(formatter, args, undefined, document.getText());
6868
edits.push(new vscode.TextEdit(getWholeFileRange(document), stdout));
69-
if (stderr) this.logger.logInfo(`fprettify error output: ${stderr}`);
69+
if (stderr) this.logger.error(`[format] fprettify error output: ${stderr}`);
7070
return edits;
7171
}
7272

@@ -81,17 +81,17 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
8181
const formatter: string = path.join(formatterPath, formatterName);
8282
// If no formatter is detected try and install it
8383
if (!which.sync(formatter, { nothrow: true })) {
84-
this.logger.logWarning(`Formatter: ${formatterName} not detected in your system.
85-
Attempting to install now.`);
84+
this.logger.warn(`[format] ${formatterName} not found! Attempting to install now.`);
8685
const msg = `Installing ${formatterName} through pip with --user option`;
8786
promptForMissingTool(formatterName, msg, 'Python', ['Install'], this.logger);
8887
}
8988

9089
const args: string[] = this.getFormatterArgs();
90+
this.logger.debug(`[format] findent args:`, args);
9191
const edits: vscode.TextEdit[] = [];
9292
const [stdout, stderr] = await spawnAsPromise(formatter, args, undefined, document.getText());
9393
edits.push(new vscode.TextEdit(getWholeFileRange(document), stdout));
94-
if (stderr) this.logger.logInfo(`findent error output: ${stderr}`);
94+
if (stderr) this.logger.error(`[format] findent error output: ${stderr}`);
9595
return edits;
9696
}
9797

@@ -107,7 +107,7 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
107107
this.formatter = this.workspace.get('formatting.formatter', 'Disabled');
108108

109109
if (!FORMATTERS.includes(this.formatter)) {
110-
this.logger.logError(`Unsupported formatter: ${this.formatter}`);
110+
this.logger.error(`[format] Unsupported formatter: ${this.formatter}`);
111111
}
112112
return this.formatter;
113113
}
@@ -130,7 +130,7 @@ export class FortranFormattingProvider implements vscode.DocumentFormattingEditP
130130
private getFormatterPath(): string {
131131
const formatterPath: string = this.workspace.get('formatting.path', '');
132132
if (formatterPath !== '') {
133-
this.logger.logInfo(`Formatter located in: ${formatterPath}`);
133+
this.logger.info(`[format] Formatter located in: ${formatterPath}`);
134134
}
135135

136136
return formatterPath;

src/features/hover-provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { CancellationToken, TextDocument, Position, Hover } from 'vscode';
2-
import { LoggingService } from '../services/logging-service';
2+
import { Logger } from '../services/logging';
33
import intrinsics from './intrinsics.json';
44

55
export class FortranHoverProvider {
6-
constructor(private loggingService: LoggingService) {}
6+
constructor(private logger: Logger) {}
77
public provideHover(
88
document: TextDocument,
99
position: Position,

src/features/linter-provider.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import * as cp from 'child_process';
55
import which from 'which';
66

77
import * as vscode from 'vscode';
8-
import { LoggingService } from '../services/logging-service';
8+
import { Logger } from '../services/logging';
99
import { FortranDocumentSelector, resolveVariables } from '../lib/tools';
1010
import * as fg from 'fast-glob';
1111
import { glob } from 'glob';
1212
import { arraysEqual } from '../lib/helper';
1313
import { RescanLint } from './commands';
1414

1515
export class FortranLintingProvider {
16-
constructor(private logger: LoggingService = new LoggingService()) {}
16+
constructor(private logger: Logger = new Logger()) {}
1717

1818
private diagnosticCollection: vscode.DiagnosticCollection;
1919
private compiler: string;
@@ -88,6 +88,7 @@ export class FortranLintingProvider {
8888
env.Path = `${path.dirname(command)}${path.delimiter}${env.Path}`;
8989
}
9090
}
91+
this.logger.info(`[lint] Compiler query command line: ${command} ${argList.join(' ')}`);
9192
const childProcess = cp.spawn(command, argList, {
9293
cwd: filePath,
9394
env: env,
@@ -101,11 +102,13 @@ export class FortranLintingProvider {
101102
compilerOutput += data;
102103
});
103104
childProcess.stderr.on('end', () => {
105+
this.logger.debug(`[lint] Compiler output:\n${compilerOutput}`);
104106
let diagnostics = this.getLinterResults(compilerOutput);
105107
diagnostics = [...new Map(diagnostics.map(v => [JSON.stringify(v), v])).values()];
106108
this.diagnosticCollection.set(textDocument.uri, diagnostics);
107109
});
108110
childProcess.on('error', err => {
111+
this.logger.error(`[lint] Compiler error:`, err);
109112
console.log(`ERROR: ${err}`);
110113
});
111114
} else {
@@ -167,7 +170,7 @@ export class FortranLintingProvider {
167170
}
168171

169172
modout = resolveVariables(modout);
170-
this.logger.logInfo(`Linter.moduleOutput: ${modFlag} ${modout}`);
173+
this.logger.debug(`[lint] moduleOutput: ${modFlag} ${modout}`);
171174
return [modFlag, modout];
172175
}
173176

@@ -195,7 +198,7 @@ export class FortranLintingProvider {
195198
// Update our cache input
196199
this.cache['includePaths'] = includePaths;
197200
// Output the original include paths
198-
this.logger.logInfo(`Linter.include:\n${includePaths.join('\r\n')}`);
201+
if (includePaths.length > 0) this.logger.debug(`[lint] include:`, includePaths);
199202
// Resolve internal variables and expand glob patterns
200203
const resIncludePaths = includePaths.map(e => resolveVariables(e));
201204
// fast-glob cannot work with Windows paths
@@ -212,12 +215,12 @@ export class FortranLintingProvider {
212215
// Try to recover from fast-glob failing due to EACCES using slower more
213216
// robust glob.
214217
} catch (eacces) {
215-
this.logger.logWarning(`You lack read permissions for an include directory
218+
this.logger.warn(`[lint] You lack read permissions for an include directory
216219
or more likely a glob match from the input 'includePaths' list. This can happen when
217220
using overly broad root level glob patters e.g. /usr/lib/** .
218221
No reason to worry. I will attempt to recover.
219222
You should consider adjusting your 'includePaths' if linting performance is slow.`);
220-
this.logger.logWarning(`${eacces.message}`);
223+
this.logger.warn(`[lint] ${eacces.message}`);
221224
try {
222225
const globIncPaths: string[] = [];
223226
for (const i of resIncludePaths) {
@@ -228,7 +231,7 @@ export class FortranLintingProvider {
228231
return globIncPaths;
229232
// if we failed again then our includes are somehow wrong. Abort
230233
} catch (error) {
231-
this.logger.logError(`Failed to recover: ${error}`);
234+
this.logger.error(`[lint] Include path glob resolution failed to recover: ${error}`);
232235
}
233236
}
234237
}
@@ -243,7 +246,7 @@ export class FortranLintingProvider {
243246
this.compiler = config.get<string>('compiler', 'gfortran');
244247
this.compilerPath = config.get<string>('compilerPath', '');
245248
if (this.compilerPath === '') this.compilerPath = which.sync(this.compiler);
246-
this.logger.logInfo(`using linter: ${this.compiler} located in: ${this.compilerPath}`);
249+
this.logger.debug(`[lint] binary: "${this.compiler}" located in: "${this.compilerPath}"`);
247250
return this.compilerPath;
248251
}
249252

@@ -287,7 +290,7 @@ export class FortranLintingProvider {
287290
const lnStr: string = ln === -1 ? 'none' : ln.toString();
288291
args.push(`-ffree-line-length-${lnStr}`, `-ffixed-line-length-${lnStr}`);
289292
}
290-
this.logger.logInfo(`Linter.arguments:\n${args.join('\r\n')}`);
293+
if (args.length > 0) this.logger.debug(`[lint] arguments:`, args);
291294

292295
// Resolve internal variables but do not apply glob pattern matching
293296
return args.map(e => resolveVariables(e));
@@ -540,7 +543,10 @@ export class FortranLintingProvider {
540543
* Regenerate the cache for the include files paths of the linter
541544
*/
542545
private rescanLinter() {
546+
this.logger.debug(`[lint] Resetting linter include paths cache`);
547+
this.logger.debug(`[lint] Current linter include paths cache:`, this.cache['includePaths']);
543548
this.cache['includePaths'] = [];
544549
this.getIncludePaths();
550+
this.logger.debug(`[lint] New linter include paths cache:`, this.cache['includePaths']);
545551
}
546552
}

0 commit comments

Comments
 (0)