diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa18d38dc81..ed479c5105a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -388,6 +388,10 @@ If you need to report an issue attach these to give the most detailed informatio 5. Now you should see additional `[debug]` prefixed logs in the output. - ![](./docs/images/logsDebugLog.png) +#### Language Server Logs + +For information related to configuring the logs produced by the AmazonQ language server, see [Language Server Logging](./docs/lsp.md#language-server-logging). + ### Telemetry - See [docs/telemetry.md](./docs/telemetry.md) for guidelines on developing telemetry in this project. diff --git a/docs/lsp.md b/docs/lsp.md index 42d94d334a4..fc7619d7bcf 100644 --- a/docs/lsp.md +++ b/docs/lsp.md @@ -26,9 +26,7 @@ sequenceDiagram ## Language Server Debugging -1. Clone https://github.com/aws/language-servers.git and set it up in the same workspace as this project by cmd+shift+p and "add folder to workspace" and selecting the language-servers folder that you just cloned. Your VS code folder structure should look like below. - - +1. Clone https://github.com/aws/language-servers.git and set it up in the same workspace as this project by cmd+shift+p and "add folder to workspace" and selecting the language-servers folder that you just cloned. Your VS code folder structure should look like below. ``` /aws-toolkit-vscode @@ -55,7 +53,14 @@ sequenceDiagram ``` 4. Uncomment the `__AMAZONQLSP_PATH` and `__AMAZONQLSP_UI` variables in the `amazonq/.vscode/launch.json` extension configuration 5. Use the `Launch LSP with Debugging` configuration and set breakpoints in VSCode or the language server -6. (Optional): Enable `"amazonq.trace.server": "on"` or `"amazonq.trace.server": "verbose"` in your VSCode settings to view detailed log messages sent to/from the language server. These log messages will show up in the "Amazon Q Language Server" output channel +6. (Optional): To customize logging options, see [Language Server Logging](#language-server-logging) + +## Language Server Logging + +There are two settings that allow us to configure the language server logging. + +- `amazonq.lsp.logLevel`: the logging level the language server should use. Options include `'error'`, `'warn'`, `'info'`, `'log'`, and `'debug'`. The default level is info. See the [implementation](<(https://github.com/aws/language-server-runtimes/blob/main/runtimes/runtimes/util/loggingUtil.ts#L4-L10)>) on the lanaguage server side for more information. +- `amazonq.lsp.traceChannel`: View detailed log messages sent to/from the language server in a seperate output channel named 'Amazon Q Language Server'. All language server logs will be routed to this output channel instead. (Requires reloading to take effect). ## Amazon Q Inline Activation diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 1d285913708..3d8524dc6cd 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -209,6 +209,23 @@ "items": { "type": "string" } + }, + "amazonQ.lsp.traceChannel": { + "type": "boolean", + "markdownDescription": "%AWS.configuration.description.amazonq.lsp.traceChannel%", + "default": false + }, + "amazonQ.lsp.logLevel": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.lsp.logLevel%", + "default": "info", + "enum": [ + "error", + "warn", + "info", + "log", + "debug" + ] } } }, diff --git a/packages/amazonq/src/lsp/chat/activation.ts b/packages/amazonq/src/lsp/chat/activation.ts index 33795219bff..1777eaa1b57 100644 --- a/packages/amazonq/src/lsp/chat/activation.ts +++ b/packages/amazonq/src/lsp/chat/activation.ts @@ -8,7 +8,7 @@ import { LanguageClient } from 'vscode-languageclient' import { AmazonQChatViewProvider } from './webviewProvider' import { registerCommands } from './commands' import { registerLanguageServerEventListener, registerMessageListeners } from './messages' -import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/shared' +import { Commands, getLogger, globals, Settings, undefinedIfEmpty } from 'aws-core-vscode/shared' import { activate as registerLegacyChatListeners } from '../../app/chat/activation' import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq' import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer' @@ -16,6 +16,7 @@ import { DidChangeConfigurationNotification, updateConfigurationRequestType, } from '@aws/language-server-runtimes/protocol' +import { getLspLogSettings, lspSettingsSection } from '../config' export async function activate(languageClient: LanguageClient, encryptionKey: Buffer, mynahUIPath: string) { const disposables = globals.context.subscriptions @@ -85,6 +86,9 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu type: 'customization', customization: undefinedIfEmpty(getSelectedCustomization().arn), }) + }), + Settings.instance.onDidChangeSection(lspSettingsSection, () => { + void pushConfigUpdate(languageClient, { type: 'logging', ...getLspLogSettings() }) }) ) } @@ -108,6 +112,10 @@ async function pushConfigUpdate(client: LanguageClient, config: QConfigs) { section: 'aws.q', settings: { customization: config.customization }, }) + } else if (config.type === 'logging') { + client.sendNotification(DidChangeConfigurationNotification.type.method, { + section: 'aws.logLevel', + }) } } type ProfileConfig = { @@ -118,4 +126,8 @@ type CustomizationConfig = { type: 'customization' customization: string | undefined } -type QConfigs = ProfileConfig | CustomizationConfig +interface LoggingConfig extends ReturnType { + type: 'logging' +} + +type QConfigs = ProfileConfig | CustomizationConfig | LoggingConfig diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 1373c026a9c..9521dac445d 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -22,7 +22,6 @@ import { } from '@aws/language-server-runtimes/protocol' import { AuthUtil, CodeWhispererSettings, getSelectedCustomization } from 'aws-core-vscode/codewhisperer' import { - Settings, oidcClientName, createServerOptions, globals, @@ -36,6 +35,7 @@ import { } from 'aws-core-vscode/shared' import { activate } from './chat/activation' import { AmazonQResourcePaths } from './lspInstaller' +import { getLspLogSettings } from './config' const localize = nls.loadMessageBundle() const logger = getLogger('amazonqLsp.lspClient') @@ -61,11 +61,9 @@ export async function startLanguageServer( serverModule, execArgv: argv, }) - + const clientId = `amazonq` const documentSelector = [{ scheme: 'file', language: '*' }] - - const clientId = 'amazonq' - const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) + const lspLogSettings = getLspLogSettings() await validateNodeExe(resourcePaths.node, resourcePaths.lsp, argv, logger) @@ -114,6 +112,9 @@ export async function startLanguageServer( }, ] } + if (params.items[0].section === 'aws.logLevel') { + return [getLspLogSettings().lspLogLevel] + } return config }, }, @@ -139,6 +140,7 @@ export async function startLanguageServer( }, }, }, + logLevel: lspLogSettings.lspLogLevel, credentials: { providesBearerToken: true, }, @@ -148,7 +150,7 @@ export async function startLanguageServer( * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. * Otherwise, logs go to the regular "Amazon Q Logs" channel. */ - ...(traceServerEnabled + ...(lspLogSettings.traceChannelEnabled ? {} : { outputChannel: globals.logOutputChannel, diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 62bba1ac93d..1a630040a08 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DevSettings, getServiceEnvVarConfig } from 'aws-core-vscode/shared' +import { DevSettings, getLogger, getServiceEnvVarConfig, Settings } from 'aws-core-vscode/shared' import { LspConfig } from 'aws-core-vscode/amazonq' export interface ExtendedAmazonQLSPConfig extends LspConfig { @@ -26,3 +26,34 @@ export function getAmazonQLspConfig(): ExtendedAmazonQLSPConfig { ...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)), } } + +// Taken from language server runtimes since they are not exported: +// https://github.com/aws/language-server-runtimes/blob/eae85672c345d8adaf4c8cbd741260b8a59750c4/runtimes/runtimes/util/loggingUtil.ts#L4-L10 +const validLspLogLevels = ['error', 'warn', 'info', 'log', 'debug'] as const +export type LspLogLevel = (typeof validLspLogLevels)[number] +export const lspSettingsSection = 'amazonQ.lsp' + +export function getLspLogSettings(): { traceChannelEnabled: boolean; lspLogLevel: LspLogLevel } { + const lspSettings = Settings.instance.getSection(lspSettingsSection) + const lspLogLevel = lspSettings.get('logLevel', 'info') + const traceChannelEnabled = lspSettings.get('traceChannel', false) + + return { + traceChannelEnabled, + lspLogLevel: sanitizeLogLevel(lspLogLevel), + } +} + +export function sanitizeLogLevel(lspLogLevel: string): LspLogLevel { + if (!isValidLspLogLevel(lspLogLevel)) { + getLogger('amazonqLsp').warn( + `Invalid log level for ${lspSettingsSection}.logLevel: ${lspLogLevel}. Defaulting to 'info'.` + ) + return 'info' + } + return lspLogLevel +} + +function isValidLspLogLevel(value: unknown): value is LspLogLevel { + return typeof value === 'string' && validLspLogLevels.includes(value as LspLogLevel) +} diff --git a/packages/amazonq/test/unit/amazonq/lsp/chat/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/chat/config.test.ts new file mode 100644 index 00000000000..57110e3be12 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/chat/config.test.ts @@ -0,0 +1,21 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { sanitizeLogLevel } from '../../../../../src/lsp/config' + +describe('sanitizeLogLevel', function () { + it('should return the log level if it is valid', function () { + const logLevel = 'info' + const sanitizedLogLevel = sanitizeLogLevel(logLevel) + assert.strictEqual(sanitizedLogLevel, logLevel) + }) + + it('should default to info if it is invalid', function () { + const logLevel = 'verbose' + const sanitizedLogLevel = sanitizeLogLevel(logLevel) + assert.strictEqual(sanitizedLogLevel, 'info') + }) +}) diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 81f56a32c57..7812eaf90ce 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -98,6 +98,8 @@ "AWS.configuration.description.amazonq.workspaceIndexIgnoreFilePatterns": "File patterns to ignore when indexing your workspace files", "AWS.configuration.description.amazonq.workspaceIndexCacheDirPath": "The path to the directory that contains the cache of the index of your workspace files", "AWS.configuration.description.amazonq.ignoredSecurityIssues": "Specifies a list of code issue identifiers that Amazon Q should ignore when reviewing your workspace. Each item in the array should be a unique string identifier for a specific code issue. This allows you to suppress notifications for known issues that you've assessed and determined to be false positives or not applicable to your project. Use this setting with caution, as it may cause you to miss important security alerts.", + "AWS.configuration.description.amazonq.lsp.traceChannel": "View detailed log messages sent to/from the language server in a seperate output channel named 'Amazon Q Language Server'. All language server logs will be routed to this output channel instead. (Requires reloading to take effect)", + "AWS.configuration.description.amazonq.lsp.logLevel": "Set the logging level of the language server.", "AWS.command.apig.copyUrl": "Copy URL", "AWS.command.apig.invokeRemoteRestApi": "Invoke in the cloud", "AWS.command.apig.invokeRemoteRestApi.cn": "Invoke on Amazon", diff --git a/packages/core/src/shared/settings-amazonq.gen.ts b/packages/core/src/shared/settings-amazonq.gen.ts index 88324e10475..eebd226fcbc 100644 --- a/packages/core/src/shared/settings-amazonq.gen.ts +++ b/packages/core/src/shared/settings-amazonq.gen.ts @@ -35,7 +35,9 @@ export const amazonqSettings = { "amazonQ.workspaceIndexMaxFileSize": {}, "amazonQ.workspaceIndexCacheDirPath": {}, "amazonQ.workspaceIndexIgnoreFilePatterns": {}, - "amazonQ.ignoredSecurityIssues": {} + "amazonQ.ignoredSecurityIssues": {}, + "amazonQ.lsp.traceChannel": {}, + "amazonQ.lsp.logLevel": {} } export default amazonqSettings