Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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,534 changes: 3,351 additions & 2,183 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.215.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we able to update this to a slightly newer version? It looks like it created a bunch of churn in the package-lock.json since i'm guessing newer versions of the aws-sdk use different versions of smithy components, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes there is a newer version, will push with a revision!

"@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,89 @@
/*!
* 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 } 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()
return this.liveTailClient.cwlClient.send(command, {
abortSignal: this.liveTailClient.abortController.signal,
})
}

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,60 @@
/*!
* 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'

export class LiveTailSessionRegistry {
static #instance: LiveTailSessionRegistry

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

public constructor(private readonly registry: Map<string, LiveTailSession> = new Map()) {}

public registerLiveTailSession(session: LiveTailSession) {
if (this.doesRegistryContainLiveTailSession(session.uri)) {
throw new ToolkitError(`There is already a LiveTail session registered with uri: ${session.uri}`)
}
this.registry.set(this.uriToKey(session.uri), session)
}

public getLiveTailSessionFromUri(uri: vscode.Uri): LiveTailSession {
const session = this.registry.get(this.uriToKey(uri))
if (!session) {
throw new ToolkitError(`No LiveTail session registered for uri: ${uri} found.`)
}
return session
}

public removeLiveTailSessionFromRegistry(uri: vscode.Uri) {
this.registry.delete(this.uriToKey(uri))
}

public doesRegistryContainLiveTailSession(uri: vscode.Uri): boolean {
return this.registry.has(this.uriToKey(uri))
}

private uriToKey(uri: vscode.Uri): string {
return uri.toString()
}
}

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,139 @@
/*!
* 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'

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

let liveTailSessionRegistry: LiveTailSessionRegistry

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

it('returns LiveTailSession after setting it', async function () {
liveTailSessionRegistry.registerLiveTailSession(session)
const retrievedSession = liveTailSessionRegistry.getLiveTailSessionFromUri(session.uri)
assert.strictEqual(retrievedSession, session)
})

it('contains returns true after setting session', async function () {
liveTailSessionRegistry.registerLiveTailSession(session)
const doesContain = liveTailSessionRegistry.doesRegistryContainLiveTailSession(session.uri)
assert.strictEqual(doesContain, true)
})

it('contains returns false if session not set', async function () {
const doesContain = liveTailSessionRegistry.doesRegistryContainLiveTailSession(session.uri)
assert.strictEqual(doesContain, false)
})

it('removeLiveTailSessionFromRegistry removes session from registry ', async function () {
liveTailSessionRegistry.registerLiveTailSession(session)
assert.strictEqual(liveTailSessionRegistry.doesRegistryContainLiveTailSession(session.uri), true)
liveTailSessionRegistry.removeLiveTailSessionFromRegistry(session.uri)
assert.strictEqual(liveTailSessionRegistry.doesRegistryContainLiveTailSession(session.uri), false)
})

it('throws registering the same session twice', async function () {
liveTailSessionRegistry.registerLiveTailSession(session)
assert.throws(() => liveTailSessionRegistry.registerLiveTailSession(session))
})

it('throws cant find session', async function () {
assert.throws(() => liveTailSessionRegistry.getLiveTailSessionFromUri(session.uri))
})
})

describe('LiveTailSession URI', async function () {
it('is correct with no logStream filter, no filter pattern', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: 'test-log-group',
region: 'test-region',
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group')
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with no logStream filter, with filter pattern', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: 'test-log-group',
region: 'test-region',
logEventFilterPattern: 'test-filter',
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group:test-filter')
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with ALL logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: 'test-log-group',
region: 'test-region',
logStreamFilter: {
type: 'all',
},
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group:all')
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with prefix logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: 'test-log-group',
region: 'test-region',
logStreamFilter: {
type: 'prefix',
filter: 'test-prefix',
},
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group:prefix:test-prefix')
const uri = createLiveTailURIFromArgs(config)
assert.deepEqual(uri, expectedUri)
})

it('is correct with specific logStream filter', function () {
const config: LiveTailSessionConfiguration = {
logGroupName: 'test-log-group',
region: 'test-region',
logStreamFilter: {
type: 'specific',
filter: 'test-stream',
},
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group: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: 'test-log-group',
region: 'test-region',
logStreamFilter: {
type: 'specific',
filter: 'test-stream',
},
logEventFilterPattern: 'test-filter',
}
const expectedUri = vscode.Uri.parse('aws-cwl-lt:test-region:test-log-group: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