import { awsSignedFetch, parseJsonBody } from './client.ts'
import { redactUnknown } from './redact.ts'
import {
boundedPageSize,
inputRecord,
optionalString,
requiredString,
type AwsAuthOptions,
} from './validation.ts'
function jsonTarget(target: string, body: Record<string, unknown>) {
return {
headers: {
'content-type': 'application/x-amz-json-1.1',
'x-amz-target': target,
},
body: JSON.stringify(body),
}
}
/**
* List CloudWatch log groups. Names and stored-byte metadata only.
*
* @example
* import { listLogGroups } from 'kody:@kody/aws/logs'
* const { items } = await listLogGroups({ prefix: '/aws/lambda/' })
*/
export async function listLogGroups(
params: AwsAuthOptions & { prefix?: string; pageSize?: number } = {},
) {
const input = inputRecord(params)
const result = await awsSignedFetch({
...params,
service: 'logs',
method: 'POST',
path: '/',
...jsonTarget('Logs_20140328.DescribeLogGroups', {
logGroupNamePrefix: optionalString(input, 'prefix'),
limit: boundedPageSize(input.pageSize, 20, 50),
}),
})
const parsed = parseJsonBody(result.text) as {
logGroups?: Array<Record<string, unknown>>
nextToken?: string
}
const items = (parsed.logGroups ?? []).map((group) => ({
name: typeof group.logGroupName === 'string' ? group.logGroupName : null,
storedBytes:
typeof group.storedBytes === 'number' ? group.storedBytes : null,
retentionInDays:
typeof group.retentionInDays === 'number'
? group.retentionInDays
: null,
arn: typeof group.arn === 'string' ? group.arn : null,
}))
return {
ok: true,
region: result.region,
nextToken: parsed.nextToken ?? null,
items,
}
}
/**
* Read a short window of CloudWatch log events. Bodies that look like
* credentials are redacted. Never dumps secret object contents.
*
* @example
* import { getLogEvents } from 'kody:@kody/aws/logs'
* const { events } = await getLogEvents({ logGroupName: '/aws/lambda/example' })
*/
export async function getLogEvents(
params: AwsAuthOptions & {
logGroupName: string
logStreamName?: string
filterPattern?: string
pageSize?: number
},
) {
const input = inputRecord(params)
const logGroupName = requiredString(input, 'logGroupName')
const logStreamName = optionalString(input, 'logStreamName')
const filterPattern = optionalString(input, 'filterPattern')
const limit = boundedPageSize(input.pageSize, 20, 50)
const target = logStreamName
? 'Logs_20140328.GetLogEvents'
: 'Logs_20140328.FilterLogEvents'
const body = logStreamName
? {
logGroupName,
logStreamName,
limit,
startFromHead: false,
}
: {
logGroupName,
filterPattern,
limit,
}
const result = await awsSignedFetch({
...params,
service: 'logs',
method: 'POST',
path: '/',
...jsonTarget(target, body),
})
const parsed = parseJsonBody(result.text) as {
events?: Array<Record<string, unknown>>
nextToken?: string
nextForwardToken?: string
}
const rawEvents = parsed.events ?? []
const events = rawEvents.map((event) => ({
timestamp: typeof event.timestamp === 'number' ? event.timestamp : null,
ingestionTime:
typeof event.ingestionTime === 'number' ? event.ingestionTime : null,
logStreamName:
typeof event.logStreamName === 'string' ? event.logStreamName : null,
message:
typeof event.message === 'string'
? redactUnknown(event.message)
: null,
}))
return {
ok: true,
region: result.region,
logGroupName,
logStreamName: logStreamName ?? null,
nextToken: parsed.nextToken ?? parsed.nextForwardToken ?? null,
events,
}
}
export default listLogGroups