Skip to content

Commit

Permalink
Merge #4859 use OutputChannel log level
Browse files Browse the repository at this point in the history
  • Loading branch information
justinmk3 authored Apr 28, 2024
2 parents bfd23b9 + a7418e4 commit 4decdf6
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 150 deletions.
12 changes: 8 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,14 @@ The `aws.dev.forceDevMode` setting enables or disables Toolkit "dev mode". Witho
### Logging
- Use the `aws.dev.logfile` setting to set the logfile path to a fixed location, so you can easily
follow and filter the logfile using shell tools like `tail` and `grep`. For example in
settings.json,
- Use `getLogger()` to log debugging messages, warnings, etc.
- Example: `getLogger().error('topic: widget failed: %O', { foo: 'bar', baz: 42 })`
- Log messages are written to the extension Output channel, which you can view in vscode by visiting the "Output" panel and selecting `AWS Toolkit Logs` or `Amazon Q Logs`.
- While viewing the Output channel (`AWS Toolkit Logs` or `Amazon Q Logs`) in vscode:
- Click the "gear" icon to [select a log level](https://github.com/aws/aws-toolkit-vscode/pull/4859) ("Debug", "Info", "Error", …).
- Click the "..." icon to open the log file.
- Use the `aws.dev.logfile` setting to set the logfile path to a fixed location, so you can follow
and filter logs using shell tools like `tail` and `grep`. For example in settings.json,
```
"aws.dev.logfile": "~/awstoolkit.log",
```
Expand All @@ -328,7 +333,6 @@ The `aws.dev.forceDevMode` setting enables or disables Toolkit "dev mode". Witho
- Use the `AWS (Developer): Watch Logs` command to watch and filter Toolkit logs (including
telemetry) in VSCode.
- Only available if you enabled "dev mode" (`aws.dev.forceDevMode` setting, see above).
- Sets `aws.logLevel` to "debug".
- Enter text in the Debug Console filter box to show only log messages with that text. <br/>
<img src="./docs/images/debug-console-filter.png" alt="VSCode Debug Console" width="320"/>
Expand Down
24 changes: 0 additions & 24 deletions packages/amazonq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,6 @@
}
},
"properties": {
"aws.logLevel": {
"type": "string",
"default": "debug",
"enum": [
"error",
"warn",
"info",
"verbose",
"debug"
],
"enumDescriptions": [
"Errors Only",
"Errors and Warnings",
"Errors, Warnings, and Info",
"Errors, Warnings, Info, and Verbose",
"Errors, Warnings, Info, Verbose, and Debug"
],
"markdownDescription": "%AWS.configuration.description.logLevel%",
"cloud9": {
"cn": {
"markdownDescription": "%AWS.configuration.description.logLevel.cn%"
}
}
},
"amazonQ.telemetry": {
"type": "boolean",
"default": true,
Expand Down
24 changes: 0 additions & 24 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,30 +90,6 @@
"default": false,
"markdownDescription": "%AWS.configuration.description.samcli.legacyDeploy%"
},
"aws.logLevel": {
"type": "string",
"default": "debug",
"enum": [
"error",
"warn",
"info",
"verbose",
"debug"
],
"enumDescriptions": [
"Errors Only",
"Errors and Warnings",
"Errors, Warnings, and Info",
"Errors, Warnings, Info, and Verbose",
"Errors, Warnings, Info, Verbose, and Debug"
],
"markdownDescription": "%AWS.configuration.description.logLevel%",
"cloud9": {
"cn": {
"markdownDescription": "%AWS.configuration.description.logLevel.cn%"
}
}
},
"aws.telemetry": {
"type": "boolean",
"default": true,
Expand Down
2 changes: 0 additions & 2 deletions packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
"AWS.configuration.profileDescription": "The name of the credential profile to obtain credentials from.",
"AWS.configuration.description.lambda.recentlyUploaded": "Recently selected Lambda upload targets.",
"AWS.configuration.description.ecs.openTerminalCommand": "The command to run when starting a new interactive terminal session.",
"AWS.configuration.description.logLevel": "AWS IDE Extensions log level (changes reflected on restart)",
"AWS.configuration.description.logLevel.cn": "The Amazon Toolkit's log level (changes reflected on restart)",
"AWS.configuration.description.iot.maxItemsPerPage": "Controls how many IoT Things, Certificates, or Policies are listed before showing a node to `Load More...`.",
"AWS.configuration.description.s3.maxItemsPerPage": "Controls how many S3 items are listed before showing a node to `Load More...`.\nThis corresponds to the `MaxKeys` requested in a single call to S3. [Learn More](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html#AmazonS3-ListObjectsV2-response-MaxKeys)",
"AWS.configuration.description.samcli.lambdaTimeout": "Maximum time (in milliseconds) to wait for SAM output while starting a Local Lambda session",
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/codewhisperer/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { debounceStartSecurityScan } from './commands/startSecurityScan'
import { securityScanLanguageContext } from './util/securityScanLanguageContext'
import { registerWebviewErrorHandler } from '../webviews/server'
import { logAndShowWebviewError } from '../shared/utilities/logAndShowUtils'
import { openSettings } from '../shared/settings'

let localize: nls.LocalizeFunc

Expand Down Expand Up @@ -190,7 +191,7 @@ export async function activate(context: ExtContext): Promise<void> {
`@id:amazonQ.showInlineCodeSuggestionsWithCodeReferences`
)
} else {
await vscode.commands.executeCommand('workbench.action.openSettings', `amazonQ`)
await openSettings('amazonQ')
}
}),
Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean = false) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const stopScanButton = localize('aws.codewhisperer.stopscan', 'Stop Scan'
const getLogOutputChan = once(() => {
const codeScanOutpuChan = vscode.window.createOutputChannel('Amazon Q Security Scan Logs')
const codeScanLogger = makeLogger({
logLevel: 'info',
outputChannels: [codeScanOutpuChan],
})
return [codeScanLogger, codeScanOutpuChan] as const
Expand Down
93 changes: 33 additions & 60 deletions packages/core/src/shared/logger/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@

import * as vscode from 'vscode'
import { Logger, LogLevel, getLogger } from '.'
import { setLogger } from './logger'
import { fromVscodeLogLevel, setLogger } from './logger'
import { WinstonToolkitLogger } from './winstonToolkitLogger'
import { Settings } from '../settings'
import { Logging } from './commands'
import { resolvePath } from '../utilities/pathUtils'
import { fsCommon } from '../../srcShared/fs'
import globals, { isWeb } from '../extensionGlobals'

export const defaultLogLevel: LogLevel = 'debug'
import { isWeb } from '../extensionGlobals'

/**
* Activate Logger functionality for the extension.
Expand All @@ -27,43 +25,41 @@ export async function activate(
const settings = Settings.instance.getSection('aws')
const devLogfile = settings.get('dev.logfile', '')
const logUri = devLogfile ? vscode.Uri.file(resolvePath(devLogfile)) : undefined
const chanLogLevel = fromVscodeLogLevel(logChannel.logLevel)

await fsCommon.mkdir(extensionContext.logUri)

const mainLogger = makeLogger(
{
logPaths: logUri ? [logUri] : undefined,
outputChannels: [logChannel],
useConsoleLog: isWeb(),
},
extensionContext.subscriptions
)
const mainLogger = makeLogger({
logLevel: chanLogLevel,
logPaths: logUri ? [logUri] : undefined,
outputChannels: [logChannel],
useConsoleLog: isWeb(),
})
logChannel.onDidChangeLogLevel?.(logLevel => {
const newLogLevel = fromVscodeLogLevel(logLevel)
mainLogger.setLogLevel(newLogLevel) // Also logs a message.
})

setLogger(mainLogger)
getLogger().info(`log level: ${getLogLevel()}`)
getLogger().info(`Log level: ${chanLogLevel}`)

// Logs to "AWS Toolkit" output channel.
setLogger(
makeLogger(
{
logPaths: logUri ? [logUri] : undefined,
outputChannels: [outputChannel, logChannel],
},
extensionContext.subscriptions
),
makeLogger({
logLevel: chanLogLevel,
logPaths: logUri ? [logUri] : undefined,
outputChannels: [outputChannel, logChannel],
}),
'channel'
)

// Logs to vscode Debug Console.
setLogger(
makeLogger(
{
staticLogLevel: 'debug',
outputChannels: [outputChannel, logChannel],
useDebugConsole: true,
},
extensionContext.subscriptions
),
makeLogger({
logLevel: chanLogLevel,
outputChannels: [outputChannel, logChannel],
useDebugConsole: true,
}),
'debugConsole'
)

Expand All @@ -75,25 +71,20 @@ export async function activate(

/**
* Creates a logger off of specified params
* @param opts Specified parameters, all optional:
* @param opts.staticLogLevel Static log level, overriding config value. Will persist overridden config value even if the config value changes.
* @param opts.logLevel Log messages at or above this level
* @param opts.logPaths Array of paths to output log entries to
* @param opts.outputChannels Array of output channels to log entries to
* @param opts.useDebugConsole If true, outputs log entries to `vscode.debug.activeDebugConsole`
* @param opts.useConsoleLog If true, outputs log entries to the nodejs or browser devtools console.
* @param disposables Array of disposables to add a subscription to
*/
export function makeLogger(
opts: {
staticLogLevel?: LogLevel
logPaths?: vscode.Uri[]
outputChannels?: vscode.OutputChannel[]
useDebugConsole?: boolean
useConsoleLog?: boolean
},
disposables?: vscode.Disposable[]
): Logger {
const logger = new WinstonToolkitLogger(opts.staticLogLevel ?? getLogLevel())
export function makeLogger(opts: {
logLevel: LogLevel
logPaths?: vscode.Uri[]
outputChannels?: vscode.OutputChannel[]
useDebugConsole?: boolean
useConsoleLog?: boolean
}): Logger {
const logger = new WinstonToolkitLogger(opts.logLevel)
// debug console can show ANSI colors, output channels can not
const stripAnsi = opts.useDebugConsole ?? false
for (const logPath of opts.logPaths ?? []) {
Expand All @@ -109,23 +100,5 @@ export function makeLogger(
logger.logToConsole()
}

if (!opts.staticLogLevel) {
vscode.workspace.onDidChangeConfiguration(
configurationChangeEvent => {
if (configurationChangeEvent.affectsConfiguration('aws.logLevel')) {
const newLogLevel = getLogLevel()
logger.setLogLevel(newLogLevel)
}
},
undefined,
disposables
)
}

return logger
}

function getLogLevel(): LogLevel {
const configuration = Settings.instance.getSection('aws')
return configuration.get(`${globals.contextPrefix}logLevel`, defaultLogLevel)
}
29 changes: 25 additions & 4 deletions packages/core/src/shared/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { Uri } from 'vscode'
import * as vscode from 'vscode'
import globals from '../extensionGlobals'

const toolkitLoggers: {
Expand All @@ -26,7 +26,7 @@ export interface Logger {
setLogLevel(logLevel: LogLevel): void
/** Returns true if the given log level is being logged. */
logLevelEnabled(logLevel: LogLevel): boolean
getLogById(logID: number, file: Uri): string | undefined
getLogById(logID: number, file: vscode.Uri): string | undefined
/** HACK: Enables logging to vscode Debug Console. */
enableDebugConsole(): void
}
Expand All @@ -48,6 +48,27 @@ const logLevels = new Map<LogLevel, number>([

export type LogLevel = 'error' | 'warn' | 'info' | 'verbose' | 'debug'

export function fromVscodeLogLevel(logLevel: vscode.LogLevel): LogLevel {
if (!vscode.LogLevel) {
// vscode version <= 1.73
return 'info'
}

switch (logLevel) {
case vscode.LogLevel.Trace:
case vscode.LogLevel.Debug:
return 'debug'
case vscode.LogLevel.Info:
return 'info'
case vscode.LogLevel.Warning:
return 'warn'
case vscode.LogLevel.Error:
case vscode.LogLevel.Off:
default:
return 'error'
}
}

/**
* Compares log levels.
*
Expand Down Expand Up @@ -96,7 +117,7 @@ export class NullLogger implements Logger {
public error(message: string | Error, ...meta: any[]): number {
return 0
}
public getLogById(logID: number, file: Uri): string | undefined {
public getLogById(logID: number, file: vscode.Uri): string | undefined {
return undefined
}
public enableDebugConsole(): void {}
Expand Down Expand Up @@ -131,7 +152,7 @@ export class ConsoleLogger implements Logger {
console.error(message, ...meta)
return 0
}
public getLogById(logID: number, file: Uri): string | undefined {
public getLogById(logID: number, file: vscode.Uri): string | undefined {
return undefined
}
public enableDebugConsole(): void {}
Expand Down
8 changes: 1 addition & 7 deletions packages/core/src/shared/logger/outputChannelTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,7 @@ export class OutputChannelTransport extends Transport {
} else if (loglevel === 'warn') {
c.warn(msg)
} else if (loglevel === 'debug' || loglevel === 'verbose') {
// XXX: `vscode.LogOutputChannel` loglevel is currently readonly:
// https://github.com/microsoft/vscode/issues/170450
// https://github.com/PowerShell/vscode-powershell/issues/4441
// So debug() will just drop messages unless the user configures vscode (via
// `code --log …` or `.vscode/argv.json` https://stackoverflow.com/a/77257398/152142).
// Use info() until vscode adds a way to set the loglevel.
c.info(msg)
c.debug(msg)
} else {
c.info(msg)
}
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/shared/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -894,11 +894,16 @@ export async function migrateSetting<T, U = T>(
await migrateForScope(vscode.ConfigurationTarget.Global)
}

/** Opens the settings UI filtered by the given prefix. */
export async function openSettings(prefix: string): Promise<void> {
await vscode.commands.executeCommand('workbench.action.openSettings', prefix)
}

/**
* Opens the settings UI at the specified key.
*
* This only works for keys that are considered "top-level", e.g. keys of {@link settingsProps}.
*/
export async function openSettings<K extends keyof SettingsProps>(key: K): Promise<void> {
export async function openSettingsId<K extends keyof SettingsProps>(key: K): Promise<void> {
await vscode.commands.executeCommand('workbench.action.openSettings', `@id:${key}`)
}
4 changes: 2 additions & 2 deletions packages/core/src/shared/telemetry/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { AwsContext } from '../awsContext'
import { DefaultTelemetryService } from './telemetryService'
import { getLogger } from '../logger'
import { getComputeRegion, isAmazonQ, isCloud9, productName } from '../extensionUtilities'
import { openSettings, Settings } from '../settings'
import { openSettingsId, Settings } from '../settings'
import { TelemetryConfig, setupTelemetryId } from './util'
import { isAutomation, isReleaseVersion } from '../vscode/env'
import { AWSProduct } from './clienttelemetry'
Expand Down Expand Up @@ -133,7 +133,7 @@ export async function handleTelemetryNoticeResponse(
// noticeResponseOk is a no-op

if (response === noticeResponseViewSettings) {
await openSettings(isAmazonQ() ? 'amazonQ.telemetry' : 'aws.telemetry')
await openSettingsId(isAmazonQ() ? 'amazonQ.telemetry' : 'aws.telemetry')
}
} catch (err) {
getLogger().error('Error while handling response from telemetry notice: %O', err as Error)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/test/shared/logger/activation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('makeLogger', function () {
before(async function () {
tempFolder = await makeTemporaryToolkitFolder()
const logPath = vscode.Uri.joinPath(vscode.Uri.file(tempFolder), 'log.txt')
testLogger = makeLogger({ staticLogLevel: 'debug', logPaths: [logPath] })
testLogger = makeLogger({ logLevel: 'debug', logPaths: [logPath] })
})

after(async function () {
Expand Down
Loading

0 comments on commit 4decdf6

Please sign in to comment.