Skip to content

Instantly share code, notes, and snippets.

@keshihoriuchi
Last active October 15, 2021 18:57
Show Gist options
  • Save keshihoriuchi/58547a80b7b9aeaf87f69474f98c438c to your computer and use it in GitHub Desktop.
Save keshihoriuchi/58547a80b7b9aeaf87f69474f98c438c to your computer and use it in GitHub Desktop.
Download CloudWatch Logs by TypeScript and AWS SDK
import {
CloudWatchLogsClient,
DescribeLogGroupsCommand,
DescribeLogStreamsCommand,
GetLogEventsCommand,
} from "@aws-sdk/client-cloudwatch-logs";
import { fromIni } from "@aws-sdk/credential-provider-ini";
import * as date from "date-fns";
import fs from "fs";
// Parameters
const profile = "default";
const logGroupNameIncludes = ["Group1"];
const logGroupNameIndex = 0;
const logStreamNameIndex = 0;
const start = "05-08 06:20";
const addHours = 1;
const out = "log.txt";
// Logics
const startDate = date.parse(start, "MM-dd HH:mm", new Date());
const startNum = date.getTime(startDate);
const endDate = date.add(startDate, { hours: addHours });
const endNum = date.getTime(endDate);
console.log(
`Download duration: ${date.formatISO(startDate)} - ${date.formatISO(endDate)}`
);
async function findLogGroupName(client: CloudWatchLogsClient) {
let counts = 0;
let nextToken: string | undefined = undefined;
for (;;) {
const command: DescribeLogGroupsCommand = new DescribeLogGroupsCommand({
nextToken,
});
const res = await client.send(command);
// console.log(res.logGroups!);
const names = res
.logGroups!.filter((lg) =>
logGroupNameIncludes.every((s) => lg.logGroupName!.includes(s))
)
.map((lg) => lg.logGroupName!);
if (counts + names.length > logGroupNameIndex) {
const lgn = names[logGroupNameIndex - counts];
return lgn;
} else {
counts += names.length;
}
if (res.nextToken) {
nextToken = res.nextToken;
} else {
return undefined;
}
}
}
async function findLogStream(client: CloudWatchLogsClient, name: string) {
let counts = 0;
let nextToken: string | undefined = undefined;
for (;;) {
const command: DescribeLogStreamsCommand = new DescribeLogStreamsCommand({
logGroupName: name,
orderBy: "LastEventTime",
descending: true,
nextToken,
});
const res = await client.send(command);
const streams = res.logStreams!;
// console.log(streams);
if (counts + streams.length > logStreamNameIndex) {
const s = streams[logStreamNameIndex - counts];
return s;
} else {
counts += streams.length;
}
if (res.nextToken) {
nextToken = res.nextToken;
} else {
return undefined;
}
}
}
async function saveLogEvents(
client: CloudWatchLogsClient,
logGroupName: string,
logStream: string
) {
const fd = await fs.promises.open(out, "w+");
let nextToken: string | undefined = undefined;
for (;;) {
const command: GetLogEventsCommand = new GetLogEventsCommand({
logGroupName,
logStreamName: logStream,
startFromHead: true,
startTime: startNum,
endTime: endNum,
nextToken,
});
const res = await client.send(command);
// console.log(res.events);
res.events!.forEach((e) => fd.write(e.message!));
if (nextToken === res.nextForwardToken) {
break;
} else {
nextToken = res.nextForwardToken;
}
}
}
(async () => {
const client = new CloudWatchLogsClient({
credentials: fromIni({ profile }),
});
const logGroupName = await findLogGroupName(client);
if (!logGroupName) {
console.error("log group name not found");
process.exit(1);
}
console.log(`logGroupName: ${logGroupName}`);
const logStream = await findLogStream(client, logGroupName);
if (!logStream) {
console.error("log stream not found");
process.exit(1);
}
console.log(`logStream: ${logStream.logStreamName!}`);
console.log(
`logStream duration: ${date.formatISO(logStream.firstEventTimestamp!)} ${date.formatISO(
logStream.lastEventTimestamp!
)}`
);
await saveLogEvents(client, logGroupName, logStream.logStreamName!);
})();
{
"name": "cloudwatch-logs-downloader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "ts-node download_cloudwatch_logs.ts"
},
"author": "Takeshi Horiuchi",
"license": "MIT",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.36.1",
"@aws-sdk/credential-provider-ini": "^3.36.1",
"date-fns": "^2.25.0",
"ts-node": "^10.3.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment