Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,539 changes: 3,354 additions & 2,185 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@
},
"dependencies": {
"@amzn/codewhisperer-streaming": "file:../../src.gen/@amzn/codewhisperer-streaming",
"@aws-sdk/client-cloudwatch-logs": "^3.666.0",
"@aws-sdk/client-cognito-identity": "^3.637.0",
"@aws-sdk/client-lambda": "^3.637.0",
"@aws-sdk/client-sso": "^3.342.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"AWS.command.downloadSchemaItemCode": "Download Code Bindings",
"AWS.command.viewLogs": "View Logs",
"AWS.command.cloudWatchLogs.searchLogGroup": "Search Log Group",
"AWS.command.cloudWatchLogs.tailLogGroup": "Tail Log Group",
"AWS.command.sam.newTemplate": "Create new SAM Template",
"AWS.command.cloudFormation.newTemplate": "Create new CloudFormation Template",
"AWS.command.quickStart": "View Quick Start",
Expand Down Expand Up @@ -231,6 +232,7 @@
"AWS.cdk.explorerTitle": "CDK",
"AWS.codecatalyst.explorerTitle": "CodeCatalyst",
"AWS.cwl.limit.desc": "Maximum amount of log entries pulled per request from CloudWatch Logs (max 10000)",
"AWS.cwl.liveTailMaxEvents.desc": "Maximum amount of log entries that can be kept in a single live tail session. When the limit is reached, the oldest events will be removed to accomodate new events (min 1000; max 10000)",
"AWS.samcli.deploy.bucket.recentlyUsed": "Buckets recently used for SAM deployments",
"AWS.submenu.amazonqEditorContextSubmenu.title": "Amazon Q",
"AWS.submenu.auth.title": "Authentication",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,7 @@ export function createURIFromArgs(
return vscode.Uri.parse(uriStr)
}

export class CloudWatchLogsSettings extends fromExtensionManifest('aws.cwl', { limit: Number }) {}
export class CloudWatchLogsSettings extends fromExtensionManifest('aws.cwl', {
limit: Number,
liveTailMaxEvents: Number,
}) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as vscode from 'vscode'
import { CloudWatchLogsClient, StartLiveTailCommand, StartLiveTailCommandOutput } from '@aws-sdk/client-cloudwatch-logs'
import { LogStreamFilterResponse } from '../liveTailLogStreamSubmenu'
import { CloudWatchLogsSettings } from '../cloudWatchLogsUtils'
import { Settings, ToolkitError } from '../../../shared'
import { createLiveTailURIFromArgs } from './liveTailSessionRegistry'

export type LiveTailSessionConfiguration = {
logGroupName: string
logStreamFilter?: LogStreamFilterResponse
logEventFilterPattern?: string
region: string
}

export type LiveTailSessionClient = {
cwlClient: CloudWatchLogsClient
abortController: AbortController
}

export class LiveTailSession {
private liveTailClient: LiveTailSessionClient
private _logGroupName: string
private logStreamFilter?: LogStreamFilterResponse
private logEventFilterPattern?: string
private _maxLines: number
private _uri: vscode.Uri

static settings = new CloudWatchLogsSettings(Settings.instance)

public constructor(configuration: LiveTailSessionConfiguration) {
this._logGroupName = configuration.logGroupName
this.logStreamFilter = configuration.logStreamFilter
this.liveTailClient = {
cwlClient: new CloudWatchLogsClient({ region: configuration.region }),
abortController: new AbortController(),
}
this._maxLines = LiveTailSession.settings.get('liveTailMaxEvents', 10000)
this._uri = createLiveTailURIFromArgs(configuration)
}

public get maxLines() {
return this._maxLines
}

public get uri() {
return this._uri
}

public get logGroupName() {
return this._logGroupName
}

public startLiveTailSession(): Promise<StartLiveTailCommandOutput> {
const command = this.buildStartLiveTailCommand()
try {
return this.liveTailClient.cwlClient.send(command, {
abortSignal: this.liveTailClient.abortController.signal,
})
} catch (e) {
throw new ToolkitError('Encountered error while trying to start LiveTail session.')
}
}

public stopLiveTailSession() {
this.liveTailClient.abortController.abort()
this.liveTailClient.cwlClient.destroy()
}

private buildStartLiveTailCommand(): StartLiveTailCommand {
let logStreamNamePrefix = undefined
let logStreamName = undefined
if (this.logStreamFilter) {
if (this.logStreamFilter.type === 'prefix') {
logStreamNamePrefix = this.logStreamFilter.filter
logStreamName = undefined
} else if (this.logStreamFilter.type === 'specific') {
logStreamName = this.logStreamFilter.filter
logStreamNamePrefix = undefined
}
}

return new StartLiveTailCommand({
logGroupIdentifiers: [this.logGroupName],
logStreamNamePrefixes: logStreamNamePrefix ? [logStreamNamePrefix] : undefined,
logStreamNames: logStreamName ? [logStreamName] : undefined,
logEventFilterPattern: this.logEventFilterPattern ? this.logEventFilterPattern : undefined,
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as vscode from 'vscode'
import { CLOUDWATCH_LOGS_LIVETAIL_SCHEME } from '../../../shared/constants'
import { LiveTailSession, LiveTailSessionConfiguration } from './liveTailSession'
import { ToolkitError } from '../../../shared'
import { NestedMap } from '../../../shared/utilities/map'

export class LiveTailSessionRegistry extends NestedMap<vscode.Uri, LiveTailSession> {
static #instance: LiveTailSessionRegistry

public static get instance() {
return (this.#instance ??= new this())
}

public constructor() {
super()
}

protected override hash(uri: vscode.Uri): string {
return uri.toString()
}

protected override get name(): string {
return LiveTailSessionRegistry.name
}

protected override get default(): LiveTailSession {
throw new ToolkitError('No LiveTailSession found for provided uri.')
}
}

export function createLiveTailURIFromArgs(sessionData: LiveTailSessionConfiguration): vscode.Uri {
let uriStr = `${CLOUDWATCH_LOGS_LIVETAIL_SCHEME}:${sessionData.region}:${sessionData.logGroupName}`

if (sessionData.logStreamFilter) {
if (sessionData.logStreamFilter.type !== 'all') {
uriStr += `:${sessionData.logStreamFilter.type}:${sessionData.logStreamFilter.filter}`
} else {
uriStr += `:${sessionData.logStreamFilter.type}`
}
}
uriStr += sessionData.logEventFilterPattern ? `:${sessionData.logEventFilterPattern}` : ''

return vscode.Uri.parse(uriStr)
}
2 changes: 2 additions & 0 deletions packages/core/src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export const ecsIamPermissionsUrl = vscode.Uri.parse(
* URI scheme for CloudWatch Logs Virtual Documents
*/
export const CLOUDWATCH_LOGS_SCHEME = 'aws-cwl' // eslint-disable-line @typescript-eslint/naming-convention
export const CLOUDWATCH_LOGS_LIVETAIL_SCHEME = 'aws-cwl-lt' // eslint-disable-line @typescript-eslint/naming-convention

export const AWS_SCHEME = 'aws' // eslint-disable-line @typescript-eslint/naming-convention

export const startLiveTailHelpUrl =
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/shared/settings-toolkit.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const toolkitSettings = {
"aws.stepfunctions.asl.maxItemsComputed": {},
"aws.ssmDocument.ssm.maxItemsComputed": {},
"aws.cwl.limit": {},
"aws.cwl.liveTailMaxEvents": {},
"aws.samcli.manuallySelectedBuckets": {},
"aws.samcli.enableCodeLenses": {},
"aws.suppressPrompts": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as vscode from 'vscode'
import assert from 'assert'
import {
LiveTailSession,
LiveTailSessionConfiguration,
} from '../../../../awsService/cloudWatchLogs/registry/liveTailSession'
import {
createLiveTailURIFromArgs,
LiveTailSessionRegistry,
} from '../../../../awsService/cloudWatchLogs/registry/liveTailSessionRegistry'
import { CLOUDWATCH_LOGS_LIVETAIL_SCHEME } from '../../../../shared/constants'

/**
* Exposes protected methods so we can test them
*/
class TestLiveTailSessionRegistry extends LiveTailSessionRegistry {
constructor() {
super()
}

override hash(uri: vscode.Uri): string {
return super.hash(uri)
}

override get default(): LiveTailSession {
return super.default
}
}

describe('LiveTailRegistry', async function () {
const session = new LiveTailSession({
logGroupName: 'test-log-group',
region: 'test-region',
})

let liveTailSessionRegistry: TestLiveTailSessionRegistry

beforeEach(function () {
liveTailSessionRegistry = new TestLiveTailSessionRegistry()
})

it('hash()', function () {
assert.deepStrictEqual(liveTailSessionRegistry.hash(session.uri), session.uri.toString())
})

it('default()', function () {
assert.throws(() => liveTailSessionRegistry.default)
})
})

describe('LiveTailSession URI', async function () {
const testLogGroupName = 'test-log-group'
const testRegion = 'test-region'
const expectedUriBase = `${CLOUDWATCH_LOGS_LIVETAIL_SCHEME}:${testRegion}:${testLogGroupName}`

it('is correct with no logStream filter, no filter pattern', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
}
const expectedUri = vscode.Uri.parse(expectedUriBase)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with no logStream filter, with filter pattern', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
logEventFilterPattern: 'test-filter',
}
const expectedUri = vscode.Uri.parse(`${expectedUriBase}:test-filter`)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with ALL logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
logStreamFilter: {
type: 'all',
},
}
const expectedUri = vscode.Uri.parse(`${expectedUriBase}:all`)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with prefix logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
logStreamFilter: {
type: 'prefix',
filter: 'test-prefix',
},
}
const expectedUri = vscode.Uri.parse(`${expectedUriBase}:prefix:test-prefix`)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with specific logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
logStreamFilter: {
type: 'specific',
filter: 'test-stream',
},
}
const expectedUri = vscode.Uri.parse(`${expectedUriBase}:specific:test-stream`)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with specific logStream filter and filter pattern', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: testLogGroupName,
region: testRegion,
logStreamFilter: {
type: 'specific',
filter: 'test-stream',
},
logEventFilterPattern: 'test-filter',
}
const expectedUri = vscode.Uri.parse(`${expectedUriBase}:specific:test-stream:test-filter`)
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})
})
7 changes: 7 additions & 0 deletions packages/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@
"description": "%AWS.cwl.limit.desc%",
"maximum": 10000
},
"aws.cwl.liveTailMaxEvents": {
"type": "number",
"default": 10000,
"description": "%AWS.cwl.liveTailMaxEvents.desc%",
"minimum": 1000,
"maximum": 10000
},
"aws.samcli.manuallySelectedBuckets": {
"type": "object",
"description": "%AWS.samcli.deploy.bucket.recentlyUsed%",
Expand Down
Loading