Skip to content

Unified logging setup for Swift extension #1746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
494 changes: 493 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,18 @@
"default": ".build/attachments",
"markdownDescription": "The path to a directory that will be used to store attachments produced during a test run.\n\nA relative path resolves relative to the root directory of the workspace running the test(s)",
"scope": "machine-overridable"
},
"swift.outputChannelLevel": {
"type": "string",
"default": "info",
"markdownDescription": "The the level of the Swift output channel. This has no affect on the verbosity of messages written to the extension's log file.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"markdownDescription": "The the level of the Swift output channel. This has no affect on the verbosity of messages written to the extension's log file.",
"markdownDescription": "The log level of the Swift output channel. This has no effect on the verbosity of messages written to the extension's log file.",

"enum": [
"debug",
"info",
"warn",
"error"
],
"scope": "machine-overridable"
}
}
},
Expand Down Expand Up @@ -927,7 +939,8 @@
"swift.diagnostics": {
"type": "boolean",
"default": false,
"markdownDescription": "Output additional diagnostics to the Swift Output View.",
"markdownDescription": "Output additional diagnostics to the Swift output channel.",
"deprecationMessage": "**Deprecated**: Please use `#swift.outputChannelLevel#` instead.",
"order": 100,
"scope": "machine-overridable"
}
Expand Down Expand Up @@ -1866,7 +1879,9 @@
"strip-ansi": "^6.0.1",
"svgo": "^4.0.0",
"tsx": "^4.20.3",
"typescript": "^5.8.3"
"typescript": "^5.8.3",
"winston": "^3.17.0",
"winston-transport": "^4.9.0"
},
"dependencies": {
"@vscode/codicons": "^0.0.38",
Expand Down
4 changes: 1 addition & 3 deletions src/DiagnosticsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ export class DiagnosticsManager implements vscode.Disposable {
);
});
})
.catch(e =>
context.outputChannel.log(`${e}`, 'Failed to provide "swiftc" diagnostics')
);
.catch(e => context.logger.error(`Failed to provide "swiftc" diagnostics: ${e}`));
});
const fileTypes = validFileTypes.join(",");
this.workspaceFileWatcher = vscode.workspace.createFileSystemWatcher(
Expand Down
8 changes: 4 additions & 4 deletions src/FolderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import { WorkspaceContext, FolderOperation } from "./WorkspaceContext";
import { BackgroundCompilation } from "./BackgroundCompilation";
import { TaskQueue } from "./tasks/TaskQueue";
import { isPathInsidePath } from "./utilities/filesystem";
import { SwiftOutputChannel } from "./ui/SwiftOutputChannel";
import { SwiftToolchain } from "./toolchain/toolchain";
import { SwiftLogger } from "./logging/SwiftLogger";

export class FolderContext implements vscode.Disposable {
private packageWatcher: PackageWatcher;
Expand Down Expand Up @@ -96,7 +96,7 @@ export class FolderContext implements vscode.Disposable {
void vscode.window.showErrorMessage(
`Failed to load ${folderContext.name}/Package.swift: ${error.message}`
);
workspaceContext.outputChannel.log(
workspaceContext.logger.info(
`Failed to load Package.swift: ${error.message}`,
folderContext.name
);
Expand Down Expand Up @@ -145,8 +145,8 @@ export class FolderContext implements vscode.Disposable {
}

/** Load Swift Plugins and store in Package */
async loadSwiftPlugins(outputChannel: SwiftOutputChannel) {
await this.swiftPackage.loadSwiftPlugins(this.toolchain, outputChannel);
async loadSwiftPlugins(logger: SwiftLogger) {
await this.swiftPackage.loadSwiftPlugins(this.toolchain, logger);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/PackageWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export class PackageWatcher {
return Version.fromString(contents.toString().trim());
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
this.workspaceContext.outputChannel.appendLine(
this.workspaceContext.logger.error(
`Failed to read .swift-version file at ${versionFile}: ${error}`
);
}
Expand Down
10 changes: 5 additions & 5 deletions src/SwiftPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { execSwift, getErrorDescription, hashString } from "./utilities/utilitie
import { isPathInsidePath } from "./utilities/filesystem";
import { SwiftToolchain } from "./toolchain/toolchain";
import { BuildFlags } from "./toolchain/BuildFlags";
import { SwiftOutputChannel } from "./ui/SwiftOutputChannel";
import { lineBreakRegex } from "./utilities/tasks";
import { SwiftLogger } from "./logging/SwiftLogger";

/** Swift Package Manager contents */
interface PackageContents {
Expand Down Expand Up @@ -308,7 +308,7 @@ export class SwiftPackage {
private static async loadPlugins(
folder: vscode.Uri,
toolchain: SwiftToolchain,
outputChannel: SwiftOutputChannel
logger: SwiftLogger
): Promise<PackagePlugin[]> {
try {
const { stdout } = await execSwift(["package", "plugin", "--list"], toolchain, {
Expand All @@ -329,7 +329,7 @@ export class SwiftPackage {
}
return plugins;
} catch (error) {
outputChannel.appendLine(`Failed to laod plugins: ${error}`);
logger.error(`Failed to load plugins: ${error}`);
// failed to load resolved file return undefined
return [];
}
Expand Down Expand Up @@ -371,8 +371,8 @@ export class SwiftPackage {
this.workspaceState = await SwiftPackage.loadWorkspaceState(this.folder);
}

public async loadSwiftPlugins(toolchain: SwiftToolchain, outputChannel: SwiftOutputChannel) {
this.plugins = await SwiftPackage.loadPlugins(this.folder, toolchain, outputChannel);
public async loadSwiftPlugins(toolchain: SwiftToolchain, logger: SwiftLogger) {
this.plugins = await SwiftPackage.loadPlugins(this.folder, toolchain, logger);
}

/** Return if has valid contents */
Expand Down
2 changes: 1 addition & 1 deletion src/SwiftSnippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export async function debugSnippetWithOptions(
return result;
});
} catch (error) {
ctx.outputChannel.appendLine(`Failed to debug snippet: ${error}`);
ctx.logger.error(`Failed to debug snippet: ${error}`);
// ignore error if task failed to run
return false;
}
Expand Down
10 changes: 5 additions & 5 deletions src/TestExplorer/TestExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export class TestExplorer {
// we fall back to discovering tests with SPM.
await this.discoverTestsInWorkspaceLSP(token);
} catch {
this.folderContext.workspaceContext.outputChannel.logDiagnostic(
this.folderContext.workspaceContext.logger.debug(
"workspace/tests LSP request not supported, falling back to SPM to discover tests.",
"Test Discovery"
);
Expand Down Expand Up @@ -300,7 +300,7 @@ export class TestExplorer {
)
.then(selected => {
if (selected === enable) {
explorer.folderContext.workspaceContext.outputChannel.log(
explorer.folderContext.workspaceContext.logger.info(
`Enabling SourceKit-LSP after swift-testing message`
);
void vscode.workspace
Expand All @@ -310,7 +310,7 @@ export class TestExplorer {
/* Put in worker queue */
});
} else if (selected === ok) {
explorer.folderContext.workspaceContext.outputChannel.log(
explorer.folderContext.workspaceContext.logger.info(
`User acknowledged that SourceKit-LSP is disabled`
);
}
Expand Down Expand Up @@ -399,7 +399,7 @@ export class TestExplorer {
} else {
explorer.setErrorTestItem(errorDescription);
}
explorer.folderContext.workspaceContext.outputChannel.log(
explorer.folderContext.workspaceContext.logger.error(
`Test Discovery Failed: ${errorDescription}`,
explorer.folderContext.name
);
Expand Down Expand Up @@ -439,7 +439,7 @@ export class TestExplorer {
* @param errorDescription Error description to display
*/
private setErrorTestItem(errorDescription: string | undefined, title = "Test Discovery Error") {
this.folderContext.workspaceContext.outputChannel.log(
this.folderContext.workspaceContext.logger.error(
`Test Discovery Error: ${errorDescription}`
);
this.controller.items.forEach(item => {
Expand Down
24 changes: 10 additions & 14 deletions src/TestExplorer/TestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ export class TestRunner {
await this.runSession(runState);
}
} catch (error) {
this.workspaceContext.outputChannel.log(`Error: ${getErrorDescription(error)}`);
this.workspaceContext.logger.error(`Error: ${getErrorDescription(error)}`);
this.testRun.appendOutput(`\r\nError: ${getErrorDescription(error)}`);
}

Expand Down Expand Up @@ -726,7 +726,7 @@ export class TestRunner {
await SwiftTestingConfigurationSetup.cleanupAttachmentFolder(
this.folderContext,
testRunTime,
this.workspaceContext.outputChannel
this.workspaceContext.logger
);
});
}
Expand Down Expand Up @@ -923,11 +923,7 @@ export class TestRunner {
const xUnitParser = new TestXUnitParser(
this.folderContext.toolchain.hasMultiLineParallelTestOutput
);
const results = await xUnitParser.parse(
buffer,
runState,
this.workspaceContext.outputChannel
);
const results = await xUnitParser.parse(buffer, runState, this.workspaceContext.logger);
if (results) {
this.testRun.appendOutput(
`\r\nExecuted ${results.tests} tests, with ${results.failures} failures and ${results.errors} errors.\r\n`
Expand Down Expand Up @@ -1007,7 +1003,7 @@ export class TestRunner {
// output test build configuration
if (configuration.diagnostics) {
const configJSON = JSON.stringify(swiftTestBuildConfig);
this.workspaceContext.outputChannel.logDiagnostic(
this.workspaceContext.logger.debug(
`swift-testing Debug Config: ${configJSON}`,
this.folderContext.name
);
Expand All @@ -1034,7 +1030,7 @@ export class TestRunner {
// output test build configuration
if (configuration.diagnostics) {
const configJSON = JSON.stringify(xcTestBuildConfig);
this.workspaceContext.outputChannel.logDiagnostic(
this.workspaceContext.logger.debug(
`XCTest Debug Config: ${configJSON}`,
this.folderContext.name
);
Expand Down Expand Up @@ -1062,15 +1058,15 @@ export class TestRunner {

LoggingDebugAdapterTracker.setDebugSessionCallback(
session,
this.workspaceContext.outputChannel,
this.workspaceContext.logger,
output => {
outputHandler(output);
}
);

// add cancellation
const cancellation = this.testRun.token.onCancellationRequested(() => {
this.workspaceContext.outputChannel.logDiagnostic(
this.workspaceContext.logger.debug(
"Test Debugging Cancelled",
this.folderContext.name
);
Expand All @@ -1084,7 +1080,7 @@ export class TestRunner {
if (e.name !== config.name) {
return;
}
this.workspaceContext.outputChannel.logDiagnostic(
this.workspaceContext.logger.debug(
"Stop Test Debugging",
this.folderContext.name
);
Expand Down Expand Up @@ -1113,7 +1109,7 @@ export class TestRunner {
this.testRun.testRunStarted();
}

this.workspaceContext.outputChannel.logDiagnostic(
this.workspaceContext.logger.debug(
"Start Test Debugging",
this.folderContext.name
);
Expand All @@ -1137,7 +1133,7 @@ export class TestRunner {
await SwiftTestingConfigurationSetup.cleanupAttachmentFolder(
this.folderContext,
testRunTime,
this.workspaceContext.outputChannel
this.workspaceContext.logger
);
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/TestExplorer/TestXUnitParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import * as xml2js from "xml2js";
import { ITestRunState } from "./TestParsers/TestRunState";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
import { SwiftLogger } from "../logging/SwiftLogger";

export interface TestResults {
tests: number;
Expand Down Expand Up @@ -50,14 +50,14 @@ export class TestXUnitParser {
async parse(
buffer: string,
runState: ITestRunState,
outputChannel: SwiftOutputChannel
logger: SwiftLogger
): Promise<TestResults | undefined> {
const xml = await xml2js.parseStringPromise(buffer);
try {
return await this.parseXUnit(xml, runState);
} catch (error) {
// ignore error
outputChannel.appendLine(`Error parsing xUnit output: ${error}`);
logger.error(`Error parsing xUnit output: ${error}`);
return undefined;
}
}
Expand Down
22 changes: 11 additions & 11 deletions src/WorkspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import * as vscode from "vscode";
import * as path from "path";
import { FolderContext } from "./FolderContext";
import { StatusItem } from "./ui/StatusItem";
import { SwiftOutputChannel } from "./ui/SwiftOutputChannel";
import { swiftLibraryPathKey } from "./utilities/utilities";
import { isExcluded, isPathInsidePath } from "./utilities/filesystem";
import { LanguageClientToolchainCoordinator } from "./sourcekit-lsp/LanguageClientToolchainCoordinator";
Expand All @@ -37,6 +36,8 @@ import { isValidWorkspaceFolder, searchForPackages } from "./utilities/workspace
import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider";
import { SwiftTaskProvider } from "./tasks/SwiftTaskProvider";
import { LLDBDebugConfigurationProvider } from "./debugger/debugAdapterFactory";
import { SwiftLogger } from "./logging/SwiftLogger";
import { SwiftLoggerFactory } from "./logging/SwiftLoggerFactory";

/**
* Context for whole workspace. Holds array of contexts for each workspace folder
Expand Down Expand Up @@ -71,12 +72,15 @@ export class WorkspaceContext implements vscode.Disposable {
public onDidStartBuild = this.buildStartEmitter.event;
public onDidFinishBuild = this.buildFinishEmitter.event;

public loggerFactory: SwiftLoggerFactory;

private constructor(
extensionContext: vscode.ExtensionContext,
public tempFolder: TemporaryFolder,
public outputChannel: SwiftOutputChannel,
public logger: SwiftLogger,
public globalToolchain: SwiftToolchain
) {
this.loggerFactory = new SwiftLoggerFactory(extensionContext.logUri);
this.statusItem = new StatusItem();
this.buildStatus = new SwiftBuildStatus(this.statusItem);
this.languageClientManager = new LanguageClientToolchainCoordinator(this, {
Expand All @@ -88,11 +92,7 @@ export class WorkspaceContext implements vscode.Disposable {
this.diagnostics = new DiagnosticsManager(this);
this.taskProvider = new SwiftTaskProvider(this);
this.pluginProvider = new SwiftPluginTaskProvider(this);
this.launchProvider = new LLDBDebugConfigurationProvider(
process.platform,
this,
outputChannel
);
this.launchProvider = new LLDBDebugConfigurationProvider(process.platform, this, logger);
this.documentation = new DocumentationManager(extensionContext, this);
this.currentDocument = null;
this.commentCompletionProvider = new CommentCompletionProviders();
Expand Down Expand Up @@ -199,7 +199,7 @@ export class WorkspaceContext implements vscode.Disposable {
this.diagnostics,
this.documentation,
this.languageClientManager,
this.outputChannel,
this.logger,
this.statusItem,
this.buildStatus,
];
Expand Down Expand Up @@ -230,11 +230,11 @@ export class WorkspaceContext implements vscode.Disposable {
/** Get swift version and create WorkspaceContext */
static async create(
extensionContext: vscode.ExtensionContext,
outputChannel: SwiftOutputChannel,
logger: SwiftLogger,
toolchain: SwiftToolchain
): Promise<WorkspaceContext> {
const tempFolder = await TemporaryFolder.create();
return new WorkspaceContext(extensionContext, tempFolder, outputChannel, toolchain);
return new WorkspaceContext(extensionContext, tempFolder, logger, toolchain);
}

/**
Expand Down Expand Up @@ -446,7 +446,7 @@ export class WorkspaceContext implements vscode.Disposable {
// find context with root folder
const index = this.folders.findIndex(context => context.folder.fsPath === folder.fsPath);
if (index !== -1) {
this.outputChannel.log(`Adding package folder ${folder} twice`, "WARN");
this.logger.warn(`Adding package folder ${folder} twice`);
return this.folders[index];
}
const folderContext = await FolderContext.create(folder, workspaceFolder, this);
Expand Down
Loading