diff --git a/extensions/jupyter-adapter/src/JupyterKernel.ts b/extensions/jupyter-adapter/src/JupyterKernel.ts index e811ca45da2..c4435bc8014 100644 --- a/extensions/jupyter-adapter/src/JupyterKernel.ts +++ b/extensions/jupyter-adapter/src/JupyterKernel.ts @@ -381,6 +381,10 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable { return this.establishSocketListeners(); } + public getKernelLogFilePath() { + return this._session!.logFile; + } + private handleIopubMsg(args: any[]) { // Deserialize the message const msg = deserializeJupyterMessage(args, this._session!.key, this._channel); diff --git a/extensions/jupyter-adapter/src/JupyterSession.ts b/extensions/jupyter-adapter/src/JupyterSession.ts index 6361cafefcd..552dfdcf80a 100644 --- a/extensions/jupyter-adapter/src/JupyterSession.ts +++ b/extensions/jupyter-adapter/src/JupyterSession.ts @@ -59,6 +59,10 @@ export class JupyterSession implements vscode.Disposable { return this.state.sessionId; } + get logFile(): string { + return this.state.logFile; + } + get portsInUse(): Array { return [ this.spec.control_port, diff --git a/extensions/jupyter-adapter/src/LanguageRuntimeAdapter.ts b/extensions/jupyter-adapter/src/LanguageRuntimeAdapter.ts index 91c223d4755..3c123724e1a 100644 --- a/extensions/jupyter-adapter/src/LanguageRuntimeAdapter.ts +++ b/extensions/jupyter-adapter/src/LanguageRuntimeAdapter.ts @@ -1029,4 +1029,8 @@ export class LanguageRuntimeSessionAdapter // Tell the kernel to shut down await this._kernel.dispose(); } + + public getKernelLogFile(): string { + return this._kernel.getKernelLogFilePath() + } } diff --git a/extensions/jupyter-adapter/src/jupyter-adapter.d.ts b/extensions/jupyter-adapter/src/jupyter-adapter.d.ts index 958461f605c..ad9016a0c02 100644 --- a/extensions/jupyter-adapter/src/jupyter-adapter.d.ts +++ b/extensions/jupyter-adapter/src/jupyter-adapter.d.ts @@ -82,6 +82,11 @@ export interface JupyterLanguageRuntimeSession extends positron.LanguageRuntimeS * response. */ callMethod(method: string, ...args: Array): Promise; + + /** + * Return logfile path + */ + getKernelLogFile(): string; } /** diff --git a/extensions/positron-python/src/client/common/utils/localize.ts b/extensions/positron-python/src/client/common/utils/localize.ts index 55adf77930d..f4f84f46349 100644 --- a/extensions/positron-python/src/client/common/utils/localize.ts +++ b/extensions/positron-python/src/client/common/utils/localize.ts @@ -530,3 +530,9 @@ export namespace CreateEnv { ); } } + +// --- Start Positron --- +export namespace Console { + export const consoleExit = l10n.t('Console exited unexpectedly. View logs for more info.'); +} +// --- End Positron --- diff --git a/extensions/positron-python/src/client/jupyter-adapter.d.ts b/extensions/positron-python/src/client/jupyter-adapter.d.ts index a67f508e14e..e9ce04b2135 100644 --- a/extensions/positron-python/src/client/jupyter-adapter.d.ts +++ b/extensions/positron-python/src/client/jupyter-adapter.d.ts @@ -78,6 +78,11 @@ export interface JupyterLanguageRuntimeSession extends positron.LanguageRuntimeS * response. */ callMethod(method: string, ...args: Array): Promise; + + /** + * Return logfile path + */ + getKernelLogFile(): string; } /** diff --git a/extensions/positron-python/src/client/positron/session.ts b/extensions/positron-python/src/client/positron/session.ts index 6a9fdf0bf86..1ea25370343 100644 --- a/extensions/positron-python/src/client/positron/session.ts +++ b/extensions/positron-python/src/client/positron/session.ts @@ -7,6 +7,7 @@ // eslint-disable-next-line import/no-unresolved import * as positron from 'positron'; +import * as fs from 'fs'; import * as vscode from 'vscode'; import PQueue from 'p-queue'; import { ProductNames } from '../common/installer/productNames'; @@ -32,6 +33,8 @@ import { JediLanguageServerAnalysisOptions } from '../activation/jedi/analysisOp import { ILanguageServerOutputChannel } from '../activation/types'; import { IWorkspaceService } from '../common/application/types'; import { IInterpreterService } from '../interpreter/contracts'; +import { showErrorMessage } from '../common/vscodeApis/windowApis'; +import { Console } from '../common/utils/localize'; /** * A Positron language runtime that wraps a Jupyter kernel and a Language Server @@ -435,8 +438,11 @@ export class PythonRuntimeSession implements positron.LanguageRuntimeSession, vs kernel.onDidReceiveRuntimeMessage((message) => { this._messageEmitter.fire(message); }); - kernel.onDidEndSession((exit) => { + kernel.onDidEndSession(async (exit) => { this._exitEmitter.fire(exit); + if (exit.exit_code !== 0) { + await this.showExitMessageWithLogs(kernel); + } }); return kernel; } @@ -490,6 +496,26 @@ export class PythonRuntimeSession implements positron.LanguageRuntimeSession, vs } } } + + private async showExitMessageWithLogs(kernel: JupyterLanguageRuntimeSession): Promise { + const logFilePath = kernel.getKernelLogFile(); + + if (fs.existsSync(logFilePath)) { + const lines = fs.readFileSync(logFilePath, 'utf8').split('\n'); + // last line of logs before generated log tail + const lastLine = lines.length - 3; + const logFileContent = lines.slice(lastLine - 1, lastLine).join('\n'); + + // see if obvious error, otherwise use generic text to logs + const regex = /^(\w*Error)\b/m; + const errortext = regex.test(logFileContent) ? logFileContent : Console.consoleExit; + + const res = await showErrorMessage(errortext, vscode.l10n.t('Open logs')); + if (res) { + kernel.showOutput(); + } + } + } } export function createJupyterKernelExtra(): undefined { diff --git a/extensions/positron-r/src/jupyter-adapter.d.ts b/extensions/positron-r/src/jupyter-adapter.d.ts index 9f98fe69e1c..ffba6491227 100644 --- a/extensions/positron-r/src/jupyter-adapter.d.ts +++ b/extensions/positron-r/src/jupyter-adapter.d.ts @@ -87,6 +87,11 @@ export interface JupyterLanguageRuntimeSession extends positron.LanguageRuntimeS * response. */ callMethod(method: string, ...args: Array): Promise; + + /** + * Return logfile path + */ + getKernelLogFile(): string; } /** diff --git a/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts b/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts index ba2a8caf861..fe62f776778 100644 --- a/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts +++ b/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts @@ -18,8 +18,9 @@ import { ISettableObservable, observableValue } from 'vs/base/common/observableI import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILifecycleService, ShutdownReason, StartupKind } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IPositronNewProjectService } from 'vs/workbench/services/positronNewProject/common/positronNewProject'; @@ -84,6 +85,7 @@ export class RuntimeStartupService extends Disposable implements IRuntimeStartup constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, + @ICommandService commandService: ICommandService, @IExtensionService private readonly _extensionService: IExtensionService, @ILanguageService private readonly _languageService: ILanguageService, @ILanguageRuntimeService private readonly _languageRuntimeService: ILanguageRuntimeService, @@ -258,7 +260,6 @@ export class RuntimeStartupService extends Disposable implements IRuntimeStartup } } }); - // Register a shutdown event handler to clear the workspace sessions to // prepare for a clean start of Positron next time. this._lifecycleService.onBeforeShutdown((e) => { @@ -825,7 +826,16 @@ export class RuntimeStartupService extends Disposable implements IRuntimeStartup action, exit.exit_code ); - this._notificationService.warn(msg); + + this._notificationService.prompt(Severity.Warning, msg, [ + { + label: nls.localize('openOutputLogs', 'Open logs'), + run: () => { + session.showOutput() + } + }, + ]); + } }