Skip to content

Instantly share code, notes, and snippets.

@daverickdunn
Created July 17, 2023 10:47
Show Gist options
  • Save daverickdunn/c05f5e1ef2493082582c6a41cdee6c85 to your computer and use it in GitHub Desktop.
Save daverickdunn/c05f5e1ef2493082582c6a41cdee6c85 to your computer and use it in GitHub Desktop.
Get Individual Lambda Costs
import {
CloudWatchLogsClient,
StartQueryCommand,
GetQueryResultsCommand,
LogGroup,
DescribeLogGroupsCommand,
ResultField
} from '@aws-sdk/client-cloudwatch-logs';
import { writeFileSync } from 'fs';
import { DateTime } from 'luxon';
const client = new CloudWatchLogsClient({});
async function query(logGroupNames: string[], days: number) {
const end = DateTime.now().toUnixInteger();
const start = DateTime.now().minus({ days }).toUnixInteger();
const command = new StartQueryCommand({
endTime: end,
logGroupNames,
queryString: `
filter @type = "REPORT"
| stats
count(@type) as countInvocations,
sum(@billedDuration) / 1000 as allDurationInSeconds,
max(@memorySize) / 1024 / 1024 / 1024 as memoryAllocated,
allDurationInSeconds * memoryAllocated * 0.0000166667 as totalCost,
totalCost / countInvocations as avgCostPerInvocation
by @log
| sort by totalCost desc
`,
startTime: start
})
const query = await client.send(command)
do {
const command = new GetQueryResultsCommand({
queryId: query.queryId
});
const res = await client.send(command)
if (res.status === 'Running') {
await new Promise(res => setTimeout(() => res(null), 2000))
} else {
return res
}
} while (true)
}
// Takes two command line args:
// 1 - LogGroup name prefix (required)
// 2 - Number of days to calcluate, from today (optional - defaults to 7)
// Usage example:
// node costs.js "/aws/lambda/prod" 7
async function main() {
const args = process.argv.slice(2);
if (typeof args[0] !== 'string') {
console.log('missing "logGroupNamePrefix" argument')
return;
}
if (args[1] !== undefined && Number.isNaN(parseInt(args[1]))) {
console.log('"days" arg must be a number')
return;
}
const logGroupNamePrefix = args[0];
const days = parseInt(args[1]) ?? 7;
let groups: LogGroup[] = [];
let groupNextToken: string | undefined = undefined;
do {
const command = new DescribeLogGroupsCommand({
logGroupNamePrefix,
nextToken: groupNextToken
})
const { logGroups, nextToken } = await client.send(command);
groups = groups.concat(logGroups);
groupNextToken = nextToken;
} while (groupNextToken !== undefined);
let queryResults: ResultField[][] = [];
let groupNames = groups.map(group => group.logGroupName);
do {
const logGroupNames = groupNames.splice(0, 50);
const { results } = await query(logGroupNames, days);
queryResults = queryResults.concat(results);
} while (groupNames.length > 0)
const result = queryResults.reduce((acc, item) => {
const name = item.find(e => e.field === '@log').value;
const cost = parseFloat(item.find(e => e.field === 'totalCost').value);
acc.totalCost += cost;
acc.groupCosts.push({ name, cost });
return acc;
}, { totalCost: 0, groupCosts: [] })
result.groupCosts = result.groupCosts.sort((a, b) => a.cost > b.cost ? -1 : 1)
writeFileSync('costs.json', JSON.stringify(result))
console.log(result)
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment