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) { 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> 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> 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