Skip to content

Commit db673c9

Browse files
authored
feat(amazonq): sync lsp logging with local configuration. (#7186)
## Problem Same problem as #7172 ## Solution - listen to logLevel change events, and forward them to LSP. - pass initial logLevel to the LSP as well. - This requires mapping the local levels to the Flare Levels. This is done with the following: ``` export const lspLogLevelMapping: Map<vscode.LogLevel, LspLogLevel> = new Map([ [vscode.LogLevel.Error, 'error'], [vscode.LogLevel.Warning, 'warn'], [vscode.LogLevel.Info, 'info'], [vscode.LogLevel.Debug, 'log'], [vscode.LogLevel.Trace, 'debug'], [vscode.LogLevel.Off, 'error'], // TODO: once the language server supports a no-log setting, we can map to that. ]) ``` ## Notes - Because of this mapping, it means that to enabled 'debug' logs in Flare, we set to 'trace' in VSC which can be confusing for contributors. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent c6afcb6 commit db673c9

File tree

5 files changed

+105
-54
lines changed

5 files changed

+105
-54
lines changed

packages/amazonq/src/lsp/chat/activation.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
8585
type: 'customization',
8686
customization: undefinedIfEmpty(getSelectedCustomization().arn),
8787
})
88+
}),
89+
globals.logOutputChannel.onDidChangeLogLevel((logLevel) => {
90+
getLogger('amazonqLsp').info(`Local log level changed to ${logLevel}, notifying LSP`)
91+
void pushConfigUpdate(languageClient, {
92+
type: 'logLevel',
93+
})
8894
})
8995
)
9096
}
@@ -98,16 +104,24 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
98104
* push the given config.
99105
*/
100106
async function pushConfigUpdate(client: LanguageClient, config: QConfigs) {
101-
if (config.type === 'profile') {
102-
await client.sendRequest(updateConfigurationRequestType.method, {
103-
section: 'aws.q',
104-
settings: { profileArn: config.profileArn },
105-
})
106-
} else if (config.type === 'customization') {
107-
client.sendNotification(DidChangeConfigurationNotification.type.method, {
108-
section: 'aws.q',
109-
settings: { customization: config.customization },
110-
})
107+
switch (config.type) {
108+
case 'profile':
109+
await client.sendRequest(updateConfigurationRequestType.method, {
110+
section: 'aws.q',
111+
settings: { profileArn: config.profileArn },
112+
})
113+
break
114+
case 'customization':
115+
client.sendNotification(DidChangeConfigurationNotification.type.method, {
116+
section: 'aws.q',
117+
settings: { customization: config.customization },
118+
})
119+
break
120+
case 'logLevel':
121+
client.sendNotification(DidChangeConfigurationNotification.type.method, {
122+
section: 'aws.logLevel',
123+
})
124+
break
111125
}
112126
}
113127
type ProfileConfig = {
@@ -118,4 +132,7 @@ type CustomizationConfig = {
118132
type: 'customization'
119133
customization: string | undefined
120134
}
121-
type QConfigs = ProfileConfig | CustomizationConfig
135+
type LogLevelConfig = {
136+
type: 'logLevel'
137+
}
138+
type QConfigs = ProfileConfig | CustomizationConfig | LogLevelConfig

packages/amazonq/src/lsp/client.ts

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
} from 'aws-core-vscode/shared'
3737
import { activate } from './chat/activation'
3838
import { AmazonQResourcePaths } from './lspInstaller'
39+
import { ConfigSection, isValidConfigSection, toAmazonQLSPLogLevel } from './config'
3940

4041
const localize = nls.loadMessageBundle()
4142
const logger = getLogger('amazonqLsp.lspClient')
@@ -80,42 +81,11 @@ export async function startLanguageServer(
8081
*/
8182
configuration: async (params, token, next) => {
8283
const config = await next(params, token)
83-
if (params.items[0].section === 'aws.q') {
84-
const customization = undefinedIfEmpty(getSelectedCustomization().arn)
85-
/**
86-
* IMPORTANT: This object is parsed by the following code in the language server, **so
87-
* it must match that expected shape**.
88-
* https://github.com/aws/language-servers/blob/1d2ca018f2248106690438b860d40a7ee67ac728/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/configurationUtils.ts#L114
89-
*/
90-
return [
91-
{
92-
customization,
93-
optOutTelemetry: getOptOutPreference() === 'OPTOUT',
94-
projectContext: {
95-
// TODO: we might need another setting to control the actual indexing
96-
enableLocalIndexing: true,
97-
enableGpuAcceleration: CodeWhispererSettings.instance.isLocalIndexGPUEnabled(),
98-
indexWorkerThreads: CodeWhispererSettings.instance.getIndexWorkerThreads(),
99-
localIndexing: {
100-
ignoreFilePatterns: CodeWhispererSettings.instance.getIndexIgnoreFilePatterns(),
101-
maxFileSizeMB: CodeWhispererSettings.instance.getMaxIndexFileSize(),
102-
maxIndexSizeMB: CodeWhispererSettings.instance.getMaxIndexSize(),
103-
indexCacheDirPath: CodeWhispererSettings.instance.getIndexCacheDirPath(),
104-
},
105-
},
106-
},
107-
]
84+
const section = params.items[0].section
85+
if (!isValidConfigSection(section)) {
86+
return config
10887
}
109-
if (params.items[0].section === 'aws.codeWhisperer') {
110-
return [
111-
{
112-
includeSuggestionsWithCodeReferences:
113-
CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled(),
114-
shareCodeWhispererContentWithAWS: !CodeWhispererSettings.instance.isOptoutEnabled(),
115-
},
116-
]
117-
}
118-
return config
88+
return getConfigSection(section)
11989
},
12090
},
12191
},
@@ -139,6 +109,7 @@ export async function startLanguageServer(
139109
showSaveFileDialog: true,
140110
},
141111
},
112+
logLevel: toAmazonQLSPLogLevel(globals.logOutputChannel.logLevel),
142113
},
143114
credentials: {
144115
providesBearerToken: true,
@@ -296,3 +267,43 @@ function onServerRestartHandler(client: LanguageClient, auth: AmazonQLspAuth) {
296267
await auth.refreshConnection(true)
297268
})
298269
}
270+
271+
function getConfigSection(section: ConfigSection) {
272+
getLogger('amazonqLsp').debug('Fetching config section %s for language server', section)
273+
switch (section) {
274+
case 'aws.q':
275+
/**
276+
* IMPORTANT: This object is parsed by the following code in the language server, **so
277+
* it must match that expected shape**.
278+
* https://github.com/aws/language-servers/blob/1d2ca018f2248106690438b860d40a7ee67ac728/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/configurationUtils.ts#L114
279+
*/
280+
return [
281+
{
282+
customization: undefinedIfEmpty(getSelectedCustomization().arn),
283+
optOutTelemetry: getOptOutPreference() === 'OPTOUT',
284+
projectContext: {
285+
// TODO: we might need another setting to control the actual indexing
286+
enableLocalIndexing: true,
287+
enableGpuAcceleration: CodeWhispererSettings.instance.isLocalIndexGPUEnabled(),
288+
indexWorkerThreads: CodeWhispererSettings.instance.getIndexWorkerThreads(),
289+
localIndexing: {
290+
ignoreFilePatterns: CodeWhispererSettings.instance.getIndexIgnoreFilePatterns(),
291+
maxFileSizeMB: CodeWhispererSettings.instance.getMaxIndexFileSize(),
292+
maxIndexSizeMB: CodeWhispererSettings.instance.getMaxIndexSize(),
293+
indexCacheDirPath: CodeWhispererSettings.instance.getIndexCacheDirPath(),
294+
},
295+
},
296+
},
297+
]
298+
case 'aws.codeWhisperer':
299+
return [
300+
{
301+
includeSuggestionsWithCodeReferences:
302+
CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled(),
303+
shareCodeWhispererContentWithAWS: !CodeWhispererSettings.instance.isOptoutEnabled(),
304+
},
305+
]
306+
case 'aws.logLevel':
307+
return [toAmazonQLSPLogLevel(globals.logOutputChannel.logLevel)]
308+
}
309+
}

packages/amazonq/src/lsp/config.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,34 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
5+
import * as vscode from 'vscode'
66
import { DevSettings, getServiceEnvVarConfig } from 'aws-core-vscode/shared'
77
import { LspConfig } from 'aws-core-vscode/amazonq'
88

99
export interface ExtendedAmazonQLSPConfig extends LspConfig {
1010
ui?: string
1111
}
1212

13+
// Taken from language server runtimes since they are not exported:
14+
// https://github.com/aws/language-server-runtimes/blob/eae85672c345d8adaf4c8cbd741260b8a59750c4/runtimes/runtimes/util/loggingUtil.ts#L4-L10
15+
const validLspLogLevels = ['error', 'warn', 'info', 'log', 'debug'] as const
16+
export type LspLogLevel = (typeof validLspLogLevels)[number]
17+
const lspLogLevelMapping: Map<vscode.LogLevel, LspLogLevel> = new Map([
18+
[vscode.LogLevel.Error, 'error'],
19+
[vscode.LogLevel.Warning, 'warn'],
20+
[vscode.LogLevel.Info, 'info'],
21+
[vscode.LogLevel.Debug, 'log'],
22+
[vscode.LogLevel.Trace, 'debug'],
23+
[vscode.LogLevel.Off, 'error'], // TODO: once the language server supports a no-log setting, we can map to that.
24+
])
25+
26+
const configSections = ['aws.q', 'aws.codeWhisperer', 'aws.logLevel'] as const
27+
export type ConfigSection = (typeof configSections)[number]
28+
29+
export function isValidConfigSection(section: unknown): section is ConfigSection {
30+
return typeof section === 'string' && configSections.includes(section as ConfigSection)
31+
}
32+
1333
export const defaultAmazonQLspConfig: ExtendedAmazonQLSPConfig = {
1434
manifestUrl: 'https://d3akiidp1wvqyg.cloudfront.net/qAgenticChatServer/0/manifest.json', // TODO swap this back
1535
supportedVersions: '*', // TODO swap this back
@@ -26,3 +46,11 @@ export function getAmazonQLspConfig(): ExtendedAmazonQLSPConfig {
2646
...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)),
2747
}
2848
}
49+
/**
50+
* The language server logging levels do not directly match those used in VSC. Therefore, we must perform a mapping defined by {@link lspLogLevelMapping}
51+
* @param logLevel vscode log level (0-5)
52+
* @returns language server log level
53+
*/
54+
export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel {
55+
return lspLogLevelMapping.get(logLevel) ?? 'info'
56+
}

packages/core/src/shared/extensionGlobals.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { ExtensionContext, OutputChannel } from 'vscode'
6+
import { ExtensionContext, LogOutputChannel, OutputChannel } from 'vscode'
77
import { LoginManager } from '../auth/deprecated/loginManager'
88
import { AwsResourceManager } from '../dynamicResources/awsResourceManager'
99
import { AWSClientBuilder } from './awsClientBuilder'
@@ -191,7 +191,7 @@ export interface ToolkitGlobals {
191191
/**
192192
* Log messages. Use `outputChannel` for application messages.
193193
*/
194-
logOutputChannel: OutputChannel
194+
logOutputChannel: LogOutputChannel
195195
loginManager: LoginManager
196196
awsContextCommands: AwsContextCommands
197197
awsContext: AwsContext

packages/core/src/shared/logger/logger.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ const logLevels = new Map<LogLevel, number>([
105105
export type LogLevel = 'error' | 'warn' | 'info' | 'verbose' | 'debug'
106106

107107
export function fromVscodeLogLevel(logLevel: vscode.LogLevel): LogLevel {
108-
if (!vscode.LogLevel) {
109-
// vscode version <= 1.73
110-
return 'info'
111-
}
112-
113108
switch (logLevel) {
114109
case vscode.LogLevel.Trace:
115110
case vscode.LogLevel.Debug:

0 commit comments

Comments
 (0)