Skip to content
Closed
6 changes: 2 additions & 4 deletions docs/lsp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -55,7 +53,7 @@ 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): Enable `"amazonq.lsp.traceChannel": "on"` 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. `"amazonq.lsp.logLevel"` can be used to configure the log level for the language server.

## Amazon Q Inline Activation

Expand Down
17 changes: 17 additions & 0 deletions packages/amazonq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,23 @@
"items": {
"type": "string"
}
},
"amazonQ.lsp.traceChannel": {
Copy link
Contributor

@nkomonen-amazon nkomonen-amazon Apr 28, 2025

Choose a reason for hiding this comment

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

Should this be explained somewhere in the documentation? Also I don't see traceChannel being used specifically in the code anywhere

Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't amazonq.lsp.trace defined here? Does it not need to be, or we want to hide from users?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed amazonq.lsp.trace to amazonQ.lsp.traceChannel however didn't update the reference in the docs. Good catch, should be updated now.

"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"
]
}
}
},
Expand Down
16 changes: 14 additions & 2 deletions packages/amazonq/src/lsp/chat/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ 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'
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
Expand Down Expand Up @@ -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() })
})
)
}
Expand All @@ -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 = {
Expand All @@ -118,4 +126,8 @@ type CustomizationConfig = {
type: 'customization'
customization: string | undefined
}
type QConfigs = ProfileConfig | CustomizationConfig
interface LoggingConfig extends ReturnType<typeof getLspLogSettings> {
type: 'logging'
}

type QConfigs = ProfileConfig | CustomizationConfig | LoggingConfig
14 changes: 8 additions & 6 deletions packages/amazonq/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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')
Expand All @@ -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)

Expand Down Expand Up @@ -114,6 +112,9 @@ export async function startLanguageServer(
},
]
}
if (params.items[0].section === 'aws.logLevel') {
return [getLspLogSettings().lspLogLevel]
}
return config
},
},
Expand All @@ -139,6 +140,7 @@ export async function startLanguageServer(
},
},
},
logLevel: lspLogSettings.lspLogLevel,
credentials: {
providesBearerToken: true,
},
Expand All @@ -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,
Expand Down
33 changes: 32 additions & 1 deletion packages/amazonq/src/lsp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
21 changes: 21 additions & 0 deletions packages/amazonq/test/unit/amazonq/lsp/chat/config.test.ts
Original file line number Diff line number Diff line change
@@ -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')
})
})
2 changes: 2 additions & 0 deletions packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/shared/settings-amazonq.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading