├── .env
├── .eslintrc.json
├── .DS_Store
├── src
└── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── components
│ ├── Button.tsx
│ ├── WarmButton.tsx
│ ├── badges.tsx
│ ├── Accordions.tsx
│ ├── button0.jsx
│ ├── LineChart.tsx
│ ├── Navbar.tsx
│ ├── WarmPeriodTabs.tsx
│ ├── calculations-updated.js
│ ├── email-template.tsx
│ ├── functionrow.tsx
│ └── data.js
│ ├── layout.tsx
│ ├── api
│ └── send
│ │ └── route.tsx
│ ├── home
│ ├── functionList
│ │ └── page.tsx
│ └── dataPage
│ │ ├── page.tsx
│ │ └── button1.jsx
│ ├── email
│ ├── page.module.css
│ └── page.tsx
│ ├── page.tsx
│ ├── retrievedData2.js
│ └── retrievedData.js
├── postcss.config.js
├── next.config.js
├── jest.config.js
├── __tests__
├── page.test.tsx
└── AWS_SDK.test.tsx
├── .gitignore
├── public
├── vercel.svg
└── next.svg
├── tsconfig.json
├── LICENSE
├── package.json
├── tailwind.config.ts
├── README.md
└── tailwind.config.js
/.env:
--------------------------------------------------------------------------------
1 | RESEND_API_KEY="re_XBt7n2JA_PNy4EjkECAMcmEhC1Sx9mfuZ"
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Embr/HEAD/.DS_Store
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/Embr/HEAD/src/app/favicon.ico
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | env: {
4 | },
5 | }
6 |
7 | module.exports = nextConfig
8 |
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 255, 255, 255;
7 | }
8 |
9 | @media (prefers-color-scheme: dark) {
10 | :root {
11 | --foreground-rgb: 255, 255, 255;
12 | }
13 | }
14 |
15 | body {
16 | color: rgb(var(--foreground-rgb));
17 | }
18 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // jest.config.js
2 | module.exports = {
3 | testEnvironment: 'node',
4 | verbose: true,
5 | collectCoverage: true,
6 | coverageDirectory: 'coverage',
7 | testPathIgnorePatterns: ['/node_modules/'],
8 | transform: {
9 | '^.+\\.(t|j)sx?$': '@swc/jest',
10 | },
11 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
12 | };
13 |
--------------------------------------------------------------------------------
/__tests__/page.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import Home from '../src/app/page';
4 | import '@testing-library/jest-dom/extend-expect'; // For the toBeInTheDocument matcher
5 |
6 | test('renders Navbar component', () => {
7 | render();
8 | const navbarElement = screen.getByRole('navigation');
9 | expect(navbarElement).toBeInTheDocument();
10 | });
--------------------------------------------------------------------------------
/src/app/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | //note typescript type setting in destructuring syntax
4 | export default function Button({ buttonName, onClickFunc }:{ buttonName:string, onClickFunc:Function}) {
5 | return (
6 |
onClickFunc()}
8 | className='cursor-pointer text-center align-middle font-mono bg-orange-800 w-60 p-2 rounded hover:bg-orange-500'
9 | >
10 | {buttonName}
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from 'next';
2 | import { Inter } from 'next/font/google';
3 | import './globals.css';
4 | import { Toaster } from 'react-hot-toast';
5 |
6 | const inter = Inter({ subsets: ['latin'] });
7 |
8 | export const metadata: Metadata = {
9 | title: 'Ember',
10 | description: 'Generated by BackEnd Boys',
11 | };
12 |
13 | export default function RootLayout({
14 | children,
15 | }: {
16 | children: React.ReactNode;
17 | }) {
18 | return (
19 |
20 |
21 |
22 | {children}
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx",
29 | ".next/types/**/*.ts",
30 | "src/app/home/dataPage/button1.jsx",
31 | "src/app/home/dataPage/page.tsx"
32 | , "src/app/api/send/route.tsx" ],
33 | "exclude": ["node_modules"]
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/components/WarmButton.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import React from 'react'
3 |
4 | //note typescript type setting in destructuring syntax
5 | export default function WarmButton({ buttonName }:{ buttonName:string}) {
6 |
7 | //warm function for a specific Lambda function - triggered on button click
8 | const warmFunction:Function = () => {
9 | fetch(
10 | 'https://k2j68xsjnc.execute-api.us-east-2.amazonaws.com/default/thumbnail-creator',
11 | {
12 | method: 'GET',
13 | mode: 'cors',
14 | }
15 | )
16 | .then((response) => response.json())
17 | .then((data) => console.log(data))
18 | .catch((err) => console.log(err));
19 | };
20 |
21 | return (
22 | warmFunction()}
24 | className='cursor-pointer text-center align-middle font-mono bg-orange-800 w-60 p-2 rounded hover:bg-orange-500'
25 | >
26 | {buttonName}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/api/send/route.tsx:
--------------------------------------------------------------------------------
1 | //src/app/api/send/route.tsx
2 | import { EmailTemplate } from '../../components/email-template';
3 | import { CreateEmailOptions } from '../../../../node_modules/resend/build/src/emails/interfaces/create-email-options.interface.d';
4 | import { Resend } from 'resend';
5 | import 'dotenv/config';
6 |
7 | const resend = new Resend(process.env.RESEND_API_KEY);
8 |
9 | export async function POST(request: any) {
10 | try {
11 | const body = await request.json();
12 | console.log('Body from route.tsx: ', body);
13 | const { name, email } = body;
14 | const data = await resend.emails.send({
15 | from: 'embr ',
16 | to: [`${email}`],
17 | subject: 'Hello',
18 | react: ,
19 | } as CreateEmailOptions);
20 |
21 | if (data.status === 'success') {
22 | return Response.json({ message: 'Email Successfully Sent!' });
23 | }
24 |
25 | return Response.json(data);
26 | } catch (error) {
27 | console.log('error: ', error);
28 | return Response.json({ error });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/app/home/functionList/page.tsx:
--------------------------------------------------------------------------------
1 |
2 | import { LambdaClient, ListFunctionsCommand } from "@aws-sdk/client-lambda";
3 |
4 | import React from 'react';
5 |
6 |
7 | const getLambdaNames = async () => {
8 |
9 | const listFunctions = async () => {
10 | const client = new LambdaClient({});
11 | const input = {
12 | MasterRegion: 'us-east-2',
13 | FunctionVersion: 'ALL',
14 | MaxItems: Number('10')
15 | }
16 | const command = new ListFunctionsCommand({input});
17 | const response = await client.send(command);
18 | return response
19 |
20 | };
21 | const data = await listFunctions()
22 | const dataList = data['Functions']
23 | const nameArray = []
24 | for(let i = 0; i < dataList.length; i++){
25 | nameArray.push(dataList[i]['FunctionName'])
26 | }
27 |
28 | console.log(nameArray)
29 | //
30 |
31 |
32 | return(
33 |
34 | test text
35 |
36 | {nameArray.map((el, index) => {
37 | return(
{el}
)
38 | })}
39 |
40 |
41 | )
42 | }
43 |
44 | export default getLambdaNames
45 |
--------------------------------------------------------------------------------
/src/app/components/badges.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | // import { initInfo } from '../retrievedData2.js';
3 | import { Badge } from '@tremor/react';
4 | const Badges = (initInfo) => {
5 | const getBadges = []
6 | console.log(initInfo);
7 | const lastinitinfo = initInfo[initInfo.length-1];
8 | console.log('last init info:',lastinitinfo);
9 | // const initDuration = lastinitinfo.initDuration;
10 | const initDuration = initInfo.initDuration;
11 | // const warmInvocationsDuration = lastinitinfo.warmInvocationsDuration;
12 | const warmInvocationsDuration = initInfo.warmInvocationsDuration;
13 | console.log(warmInvocationsDuration);
14 |
15 | // for(let i=0; i{warmInvocationsDuration[i]}
18 | // )
19 | // }
20 |
21 | return (
22 |
23 | {initDuration}
24 | {/* {warmInvocationsDuration.map(invocation => {invocation}ms)} */}
25 | {/* {getBadges} */}
26 |
27 | )
28 | }
29 |
30 | export default Badges
--------------------------------------------------------------------------------
/src/app/email/page.module.css:
--------------------------------------------------------------------------------
1 | /*For Email page*/
2 | .email {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | color: black;
7 | height: 100vh; /* Adjust the height as needed */
8 | background-color: #f5f5f5; /* Light gray background color */
9 | }
10 |
11 | .email p {
12 | font-size: 24px;
13 | color: #333; /* Dark gray text color */
14 | margin-bottom: 20px;
15 | }
16 |
17 | .email form {
18 | display: flex;
19 | flex-direction: column;
20 | align-items: center;
21 | width: 300px; /* Adjust the width as needed */
22 | }
23 |
24 | .email form label {
25 | font-size: 16px;
26 | color: #555; /* Medium gray text color */
27 | margin-bottom: 10px;
28 | }
29 |
30 | .email form input {
31 | width: 100%;
32 | padding: 10px;
33 | margin-bottom: 20px;
34 | border: 1px solid #ccc; /* Light gray border */
35 | }
36 |
37 | .email form button {
38 | padding: 10px 20px;
39 | background-color: #555; /* Medium gray button background color */
40 | color: #fff; /* White button text color */
41 | border: none;
42 | cursor: pointer;
43 | }
44 |
45 | .email form button:hover {
46 | background-color: #333; /* Dark gray button background color on hover */
47 | }
48 |
--------------------------------------------------------------------------------
/__tests__/AWS_SDK.test.tsx:
--------------------------------------------------------------------------------
1 | import { LambdaClient, ListFunctionsCommand } from "@aws-sdk/client-lambda";
2 | import Home from '../src/app/page';
3 |
4 | jest.mock('@aws-sdk/client-lambda', () => ({
5 | LambdaClient: jest.fn(),
6 | ListFunctionsCommand: jest.fn()
7 | }));
8 |
9 | const mockedLambdaClient = {
10 | send: jest.fn().mockResolvedValue({
11 | Functions: [
12 | { FunctionName: 'function1' },
13 | { FunctionName: 'function2' },
14 | { FunctionName: 'function3' },
15 | { FunctionName: 'function4' },
16 | { FunctionName: 'function5' }
17 | ]
18 | })
19 | };
20 |
21 | LambdaClient.mockImplementation(() => mockedLambdaClient);
22 |
23 | test('fetches and displays Lambda function names', async () => {
24 | render();
25 | expect(mockedLambdaClient.send).toHaveBeenCalledTimes(1);
26 | expect(await screen.findByText('/aws/lambda/function1')).toBeInTheDocument();
27 | expect(await screen.findByText('/aws/lambda/function2')).toBeInTheDocument();
28 | expect(await screen.findByText('/aws/lambda/function3')).toBeInTheDocument();
29 | expect(await screen.findByText('/aws/lambda/function4')).toBeInTheDocument();
30 | expect(await screen.findByText('/aws/lambda/function5')).toBeInTheDocument();
31 | });
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/components/Accordions.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Accordion,
3 | AccordionBody,
4 | AccordionHeader,
5 | Card,
6 | Flex,
7 | Metric,
8 | ProgressBar,
9 | Tab,
10 | TabGroup,
11 | TabList,
12 | TabPanel,
13 | TabPanels,
14 | Text } from '@tremor/react';
15 |
16 | import { UserGroupIcon, UserIcon } from "@heroicons/react/solid";
17 | import LineChart from './LineChart';
18 | import ourData from './data'
19 |
20 | const SingleAccordion = ({ currentDay }) => {
21 | const linechartsArray = [];
22 | for (let i = 0; i < ourData.length; i++) {
23 | if (ourData[i].day === currentDay) linechartsArray.push();
24 | }
25 |
26 | return (
27 |
28 | Date: {currentDay}
29 | {linechartsArray}
30 |
31 | );
32 | };
33 |
34 | const allAccordionArray = [];
35 |
36 | for (let i = 0; i < ourData.length; i++) {
37 | if (i === 0)
38 | allAccordionArray.push(
39 |
40 | {ourData[i].day}
41 |
42 | );
43 | else if (ourData[i].day !== ourData[i - 1].day)
44 | allAccordionArray.push(
45 |
46 | {ourData[i].day}
47 |
48 | );
49 | }
50 | export default allAccordionArray;
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "primary-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "test": "jest"
11 | },
12 | "dependencies": {
13 | "@aws-sdk/client-cloudwatch-logs": "^3.476.0",
14 | "@aws-sdk/client-lambda": "^3.474.0",
15 | "@heroicons/react": "^1.0.6",
16 | "@tremor/react": "^3.11.1",
17 | "aws": "^0.0.3-2",
18 | "aws-sdk": "^2.1520.0",
19 | "dotenv": "^16.3.1",
20 | "identity-obj-proxy": "^3.0.0",
21 | "next": "^14.0.3",
22 | "react": "^18",
23 | "react-dom": "^18",
24 | "react-hot-toast": "^2.4.1",
25 | "react-icons": "^4.12.0",
26 | "resend": "^2.0.0"
27 | },
28 | "devDependencies": {
29 | "@swc/core": "^1.7.2",
30 | "@swc/jest": "^0.2.36",
31 | "@testing-library/jest-dom": "^6.4.8",
32 | "@testing-library/react": "^16.0.0",
33 | "@testing-library/user-event": "^14.5.2",
34 | "@types/css-modules": "^1.0.5",
35 | "@types/jest": "^29.5.12",
36 | "@types/node": "^20",
37 | "@types/react": "^18",
38 | "@types/react-dom": "^18",
39 | "@types/testing-library__jest-dom": "^6.0.0",
40 | "autoprefixer": "^10.0.1",
41 | "eslint": "^8",
42 | "eslint-config-next": "14.0.3",
43 | "jest": "^29.7.0",
44 | "postcss": "^8",
45 | "tailwindcss": "^3.3.0",
46 | "typescript": "^5"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/email/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | //src/app/email/page.tsx
3 | import React, { useState } from 'react';
4 | import styles from './page.module.css';
5 | import Navbar from '../components/Navbar';
6 | import { toast } from 'react-hot-toast';
7 |
8 | const Email = () => {
9 | const [data, setData] = useState({
10 | name: '',
11 | email: '',
12 | });
13 |
14 | const sendEmail = async (e: any) => {
15 | e.preventDefault();
16 | const response = await fetch('/api/send', {
17 | method: 'POST',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | body: JSON.stringify(data),
22 | });
23 |
24 | if (response.status === 200) {
25 | setData({
26 | name: '',
27 | email: '',
28 | });
29 | toast.success(`Hey, ${data.name}, your message was sent successfully!`);
30 | }
31 | };
32 |
33 | return (
34 |
35 |
36 |
37 |
Please provide your name and Email!
38 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default Email;
65 |
--------------------------------------------------------------------------------
/src/app/components/button0.jsx:
--------------------------------------------------------------------------------
1 |
2 | const AWS = require('aws-sdk');
3 | const dotenv = require('dotenv');
4 |
5 | dotenv.config();
6 |
7 | // console.log({accessKeyId: process.env.AWS_ACCESS_KEY_ID})
8 |
9 | AWS.config.update({
10 | accessKeyId: process.env.accessKeyId,
11 | secretAccessKey: process.env.secretAccessKey,
12 | region: process.env.region,
13 | });
14 |
15 | async function GetData() {
16 | console.log('\n\n********>> inside getData <<********\n');
17 | const cloudwatchlogs = new AWS.CloudWatchLogs();
18 | const now = new Date();
19 | const oneWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // TODO: currently 24 hours ago, * 7 if want to change to week. unit is currently in seconds
20 |
21 | const params = {
22 | // when we getLogs, i am wanting to grab the Timestamp, RequestId, and DurationInMS
23 | // https://us-east-2.console.aws.amazon.com/lambda/home?region=us-east-2#/functions/titans-lambda-log-test?tab=monitoring
24 | startTime: oneWeek.getTime(),
25 | endTime: now.getTime(),
26 | queryString:
27 | 'fields @ingestionTime, @initDuration, @logStream, @message, @timestamp, @type, @billedDuration, @duration, @maxMemoryUsed, @memorySize | filter @initDuration | sort @timestamp desc | limit 2000',
28 | logGroupName: '/aws/lambda/ChrisTestFunc',
29 | };
30 | console.log(params);
31 | await cloudwatchlogs
32 | .startQuery(params)
33 | .promise()
34 | .then((promiseData) => {
35 | console.log(promiseData.queryId);
36 | setTimeout(() => {
37 | cloudwatchlogs
38 | .getQueryResults({ queryId: promiseData.queryId })
39 | .promise()
40 | .then((data) => {
41 | console.log(data.results);
42 | });
43 | }, 1000);
44 | });
45 | }
46 |
47 | export default GetData;
--------------------------------------------------------------------------------
/src/app/components/LineChart.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import {
3 | Card,
4 | Metric,
5 | Text,
6 | Divider,
7 | AreaChart,
8 | TabGroup,
9 | TabList,
10 | Tab,
11 | TabPanels,
12 | TabPanel } from '@tremor/react';
13 |
14 | const LineChart = ({ oneData }) => {
15 |
16 | const valueFormatter = (number: number | bigint) => `${Intl.NumberFormat('us').format(number).toString()}`;
17 |
18 | //Takes in log object and creates a graph point for each warm invocation
19 | const formatter = (info) => {
20 | let result = [];
21 | for (let i = 0; i < info.warmInvocationsDuration.length; i++) {
22 | //each plot point has timestamp and a runtime
23 | let entry = {
24 | //timestamp is sliced to take out the date and contain only the time (hh:mm:ss format).
25 | Timestamp: info.timestamp.slice(info.timestamp.indexOf(' ') + 1, info.timestamp.indexOf('.') - 3),
26 | Runtime: info.warmInvocationsDuration[i],
27 | };
28 | result.push(entry);
29 | }
30 | console.log(result);
31 | return result; //array of plot points
32 | };
33 |
34 | const data = formatter(oneData);
35 | return (<>
36 | {oneData.timestamp.slice(oneData.timestamp.indexOf(' ') + 1, oneData.timestamp.indexOf('.') - 3)}
37 | cold start: {oneData.initDuration}
38 |
50 | >
51 | );
52 | };
53 |
54 | export default LineChart;
--------------------------------------------------------------------------------
/src/app/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import React from 'react';
4 | import Link from 'next/link';
5 | import { SiAwslambda } from 'react-icons/si';
6 | import { RiFunctionFill } from 'react-icons/ri';
7 | import { IoHomeOutline } from 'react-icons/io5';
8 | import { MdOutlineMarkEmailRead } from 'react-icons/md';
9 |
10 | const Navbar = () => {
11 | return (
12 |
13 |
14 |
15 |
16 | -
17 |
18 |
19 |
Home
20 |
21 |
22 | -
23 |
24 |
25 |
Function
26 |
27 |
28 | -
29 |
30 |
31 |
Notification
32 |
33 |
34 | -
35 |
36 |
37 |
Lambda
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 | };
46 |
47 | export default Navbar;
48 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'tailwindcss'
2 |
3 | const config: Config = {
4 | content: [
5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}',
7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}',
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
13 | 'gradient-conic':
14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
15 | },
16 | },
17 | },
18 | safelist: [
19 | {
20 | pattern:
21 | /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
22 | variants: ["hover", "ui-selected"],
23 | },
24 | {
25 | pattern:
26 | /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
27 | variants: ["hover", "ui-selected"],
28 | },
29 | {
30 | pattern:
31 | /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
32 | variants: ["hover", "ui-selected"],
33 | },
34 | {
35 | pattern:
36 | /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
37 | },
38 | {
39 | pattern:
40 | /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
41 | },
42 | {
43 | pattern:
44 | /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
45 | },
46 | ],
47 | plugins: [],
48 | }
49 | export default config
50 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './globals.css';
3 | import Functionrow from './components/functionrow';
4 | import Navbar from './components/Navbar';
5 | import { Flex, Card, Text } from '@tremor/react';
6 |
7 | // AWS CloudWatch imports to get Lambda function names
8 | import { LambdaClient, ListFunctionsCommand } from "@aws-sdk/client-lambda";
9 |
10 | // Home page
11 | const Home = async () => {
12 |
13 | const getLambdaNames = async () => {
14 |
15 | const listFunctions = async () => {
16 | const client = new LambdaClient({});
17 | const input = {
18 | MasterRegion: 'us-east-2',
19 | FunctionVersion: 'ALL',
20 | MaxItems: Number('10')
21 | };
22 | const command = new ListFunctionsCommand({ input });
23 | const response = await client.send(command);
24 | return response;
25 |
26 | };
27 | const data = await listFunctions();
28 | const dataList = data['Functions'];
29 | const nameArray = [];
30 | for (let i = 0; i < dataList.length; i++) {
31 | nameArray.push(`/aws/lambda/${dataList[i]['FunctionName']}`);
32 | }
33 | return nameArray;
34 | };
35 |
36 | let nameArray = await getLambdaNames();
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default Home;
--------------------------------------------------------------------------------
/src/app/home/dataPage/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import Link from 'next/link';
3 | import {
4 | Flex,
5 | Bold,
6 | Card,
7 | Title,
8 | Text,
9 | Metric,
10 | BarList,
11 | Button,
12 | } from '@tremor/react';
13 | ``;
14 |
15 | import React from 'react';
16 | import { useState, useEffect } from 'react';
17 |
18 |
19 | const AWS = require('aws-sdk');
20 | const dotenv = require('dotenv');
21 |
22 | dotenv.config();
23 |
24 | // console.log({accessKeyId: process.env.AWS_ACCESS_KEY_ID})
25 |
26 | AWS.config.update({
27 | accessKeyId: process.env.accessKeyId,
28 | secretAccessKey: process.env.secretAccessKey,
29 | region: process.env.region,
30 | });
31 | function GotData() {
32 | const [newData, setNewData] = useState();
33 |
34 | useEffect(() => {
35 | async function getData() {
36 | console.log('\n\n********>> inside getData <<********\n');
37 | const cloudwatchlogs = new AWS.CloudWatchLogs();
38 | const now = new Date();
39 | const oneWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // TODO: currently 24 hours ago, * 7 if want to change to week. unit is currently in seconds
40 |
41 | const params = {
42 | // when we getLogs, i am wanting to grab the Timestamp, RequestId, and DurationInMS
43 | // https://us-east-2.console.aws.amazon.com/lambda/home?region=us-east-2#/functions/titans-lambda-log-test?tab=monitoring
44 | startTime: oneWeek.getTime(),
45 | endTime: now.getTime(),
46 | queryString:
47 | 'fields @ingestionTime, @initDuration, @logStream, @message, @timestamp, @type, @billedDuration, @duration, @maxMemoryUsed, @memorySize | sort @timestamp desc | limit 3',
48 | logGroupName: '/aws/lambda/ChrisTestFunc',
49 | };
50 |
51 | try {
52 | const queryData = await cloudwatchlogs.startQuery(params).promise();
53 | setTimeout(() => {cloudwatchlogs.getQueryResults({ queryId: queryData.queryId }).promise().then((data) => {setNewData(data.results)})}, 2000);
54 | } catch(error) {
55 | console.error('error fethcing data: ', error)
56 | }
57 | }
58 | if(!newData) {
59 | getData();
60 | }
61 | }, [newData]);
62 |
63 | return (
64 |
65 |
66 |
YEEAAHHH BOIII
67 |
68 | {
{JSON.stringify(newData, null, 2)}
}
69 | Hello
70 |
71 |
72 | )
73 | }
74 | export default GotData
75 |
--------------------------------------------------------------------------------
/src/app/home/dataPage/button1.jsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import React, { useState } from 'react';
3 |
4 | const AWS = require('aws-sdk');
5 | const dotenv = require('dotenv');
6 |
7 | dotenv.config();
8 |
9 | // console.log({accessKeyId: process.env.AWS_ACCESS_KEY_ID})
10 |
11 | AWS.config.update({
12 | accessKeyId: process.env.accessKeyId,
13 | secretAccessKey: process.env.secretAccessKey,
14 | region: process.env.region,
15 | });
16 |
17 | function GotData() {
18 | const [newData, setNewData] = useState();
19 |
20 | async function GetData() {
21 | console.log('\n\n********>> inside getData <<********\n');
22 | const cloudwatchlogs = new AWS.CloudWatchLogs();
23 | const now = new Date();
24 | const oneWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // TODO: currently 24 hours ago, * 7 if want to change to week. unit is currently in seconds
25 |
26 | const params = {
27 | // when we getLogs, i am wanting to grab the Timestamp, RequestId, and DurationInMS
28 | // https://us-east-2.console.aws.amazon.com/lambda/home?region=us-east-2#/functions/titans-lambda-log-test?tab=monitoring
29 | startTime: oneWeek.getTime(),
30 | endTime: now.getTime(),
31 | queryString:
32 | 'fields @ingestionTime, @initDuration, @logStream, @message, @timestamp, @type, @billedDuration, @duration, @maxMemoryUsed, @memorySize | sort @timestamp desc | limit 1',
33 | logGroupName: '/aws/lambda/ChrisTestFunc',
34 | };
35 | // console.log(params);
36 | await cloudwatchlogs
37 | .startQuery(params)
38 | .promise()
39 | .then((promiseData) => {
40 | console.log(promiseData.queryId);
41 | setTimeout(() => {
42 | cloudwatchlogs
43 | .getQueryResults({ queryId: promiseData.queryId })
44 | .promise()
45 | .then((data) => {
46 | setNewData(data.results);
47 | });
48 | }, 2000);
49 | });
50 | }
51 | GetData();
52 | console.log(newData);
53 |
54 | return (
55 |
56 | {/* {newData.map((el, index) => (
57 |
60 | ))} */}
61 | Hello
62 |
63 | //
64 | //
65 | //
{newData.results}
66 | //
67 | //
68 | );
69 | }
70 |
71 | export default GotData;
72 |
--------------------------------------------------------------------------------
/src/app/components/WarmPeriodTabs.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Accordion,
3 | AccordionBody,
4 | AccordionHeader,
5 | Card,
6 | Flex,
7 | Metric,
8 | ProgressBar,
9 | Tab,
10 | TabGroup,
11 | TabList,
12 | TabPanel,
13 | TabPanels,
14 | Text } from '@tremor/react';
15 |
16 | import { UserGroupIcon, UserIcon } from "@heroicons/react/solid";
17 |
18 | import LineChart from './LineChart';
19 |
20 | const mockInitInfo = [
21 | {//0
22 | timestamp: '2023-12-15 19:42:01.839',
23 | initDuration: '173.14',
24 | warmInvocationsDuration: ['1.34', '12.42'],
25 | day: '2023-12-15',
26 | },
27 | {//1
28 | timestamp: '2023-12-14 19:44:01.840',
29 | initDuration: '165.61',
30 | warmInvocationsDuration: ['5.61', '2.02','7.21'],
31 | day: '2023-12-14',
32 | },
33 | {//2
34 | timestamp: '2023-12-14 18:18:43.243',
35 | initDuration: '181.02',
36 | warmInvocationsDuration: ['1.04', '3.91', '2.36', '3.32', '5.60'],
37 | day: '2023-12-03',
38 | },
39 | {//3
40 | timestamp: '2023-12-14 17:46:43.243',
41 | initDuration: '162.05',
42 | warmInvocationsDuration: ['1.04', '1.71', '1.46','3.52'],
43 | day: '2023-12-02',
44 | },
45 | {//4
46 | timestamp: '2023-12-14 17:21:43.243',
47 | initDuration: '108.44',
48 | warmInvocationsDuration: ['14.04', '11.71', '21.33', '10.25', '13.46', '18.18'],
49 | day: '2023-12-12',
50 | },
51 | {//5
52 | timestamp: '2023-12-14 18:18:43.243',
53 | initDuration: '116.39',
54 | warmInvocationsDuration: ['11.04', '14.01', '31.92', '21.36'],
55 | day: '2023-12-13',
56 | },
57 | {//6
58 | timestamp: '2023-12-14 18:18:43.243',
59 | initDuration: '108.24',
60 | warmInvocationsDuration: ['4.04', '8.41', '3.55', '6.92', '4.86'],
61 | day: '2023-12-14',
62 | },
63 | {//7
64 | timestamp: '2023-12-06 18:18:43.243',
65 | initDuration: '122.71',
66 | warmInvocationsDuration: ['8.52', '3.51', '6.46', '3.17', '6.12'],
67 | day: '2023-12-06',
68 | },
69 | ];
70 |
71 | const WarmPeriodTabs = ({ dataIndex }) => {
72 |
73 | return (
74 |
75 | {/* Warm execution contexts */}
76 |
77 |
78 | {mockInitInfo[dataIndex].timestamp.slice(0,9)}
79 | {mockInitInfo[dataIndex+1].timestamp.slice(0,9)}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | );
94 | };
95 |
96 | export default WarmPeriodTabs;
--------------------------------------------------------------------------------
/src/app/retrievedData2.js:
--------------------------------------------------------------------------------
1 | const mockData = [
2 | [
3 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
4 | { field: '@initDuration', value: '103.01' },
5 | {
6 | field: '@logStream',
7 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
8 | },
9 | {
10 | field: '@message',
11 | value: 'END RequestId: 76269889-7d50-4d6d-a3f1-9b4f5322f47c\n',
12 | },
13 | { field: '@timestamp', value: '2023-12-05 22:14:43.877' },
14 | { field: '@type', value: 'END' },
15 | {
16 | field: '@ptr',
17 | value:
18 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAIYAQ==',
19 | },
20 | ],
21 | ]
22 | //Improves format of returned data from AWS for further processing
23 | const parseData = function (data) {
24 | const newData = [];
25 | for (let i = 0; i < data.length; i++) {
26 | // replace end-statement with data.length
27 | const newEntry = {};
28 | for (let j = 0; j < data[i].length; j++) {
29 | newEntry[data[i][j].field] = data[i][j].value;
30 | }
31 | newData.push(newEntry);
32 | }
33 | return newData;
34 | };
35 |
36 | const readableMockData = parseData(mockData);
37 |
38 | const getInitInfo = function (data) {
39 | const results = [];
40 | for (let i = 0; i < data.length; i++) {
41 | const newEntry = {}
42 | if (data[i]['@initDuration']){
43 | newEntry['timestamp'] = data[i]['@timestamp'];
44 | newEntry['initDuration'] = data[i]['@initDuration'];
45 | // newEntry['message'] = data[i]['@message'];
46 | newEntry['warmInvocationsDuration'] = [];
47 | results.push(newEntry);
48 | }
49 | // now for the rest of the loops, access the most recent newEntry[newEntry.length - 1]['timestampsofWarmIncovationsWithin].push(data[i]['@duration])
50 | else if (data[i]['@duration'] && results.length !== 0) results[results.length - 1]['warmInvocationsDuration'].push(data[i]['@duration']);
51 | }
52 | return results;
53 | };
54 |
55 | module.exports = { parseData, getInitInfo };
56 | // *************** Task 1: Getting the name of the function ************************************
57 | // the name of our s3 bucket is called original-dinner-time-images, the name of our function is called titans-lambda-log-function. this is obtainable through the variable params.logGroupName on server.js and is not information we query for on AWS CloudWatch
58 | //
59 | // *************** Task 2: Average duration of warm time / 24 hour scale ***********************
60 |
61 | // *************** Task 3: Inital duration (number - milliseconds) *****************************
62 |
63 | // *************** Task 4: Cold calls/ week (number) *******************************************
64 |
65 | // *************** Task 5: Average Initialization Duration (number - milliseconds ) ************
66 |
67 | // ********************************** - STRETCH - **********************************************
68 |
69 | // ********** Task 5: Average DurRun **********
70 |
71 | // ********** Task 6: Average DurCold **********
72 |
73 | // ********** Task 7: Average DurRun **********
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Embr
2 |
3 | ## Overview
4 | Embr provides a dynamic solution for developers seeking to better manage their serverless functions. The goal with this open-source product is to create an application that identifies all Lambda functions currently deployed in a user's account, and monitors those functions to determine usage patterns. Finally, based on usage patterns, the Embr appication allows support engineers to warm cold Lambdas before periods of expected high-use.
5 |
6 | Additional context to understand the approach for building Embr:
7 | - The primary impact of cold lambda functions is the impact to end users
8 | - Lambdas go cold after approximately 40 minutes of no use
9 | - Billing occurs by multiplying function runtime by RAM allocated
10 | - A cold Lambda requires allocation and instantiation of an execution environment
11 | - For each concurrent call to a cold function, the duration time is multiplied by each call
12 |
13 | This application is for developers looking to optimize serverless function use by maximizing downtime while minimizing the impact of cold starts on dependent functions or users.
14 |
15 | ## Description
16 | - Features
17 | - Embr connects to a user's AWS account and provides an easy-to-read interface of all detected Lambda functions
18 | - Embr tracks and graphs specifics metrics relevant to cold calls and warm calls
19 | - Embr offers a button enabling users to warm a function on demand
20 | - Tech Stack
21 | - Front-end is built using Next.js and Tailwind CSS with some components leveraged from the Tremor library
22 | - AWS Cloudwatch used to query the Lambda metadata, which is further processed before used for visualizations
23 | - No local or cloud data storage! All data comes directly from AWS, ensuring maximum security
24 | - Coming Soon!
25 | - Allow users to combine and manage serverless functions across multiple regions
26 | - Offer a cloud-hosted version of the application
27 | - Enable notifications for anomoulous Lambda usage
28 |
29 | ## How to Use Embr
30 | 1. Fork, clone, and install all dependencies
31 | 2. Install latest version of AWS CLI
32 |
33 | - https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
34 | 3. Using the terminal, run 'aws configure' and add your access information to AWS - e.g.:
35 | - AWS Access Key ID [****************MBI3]:
36 | - AWS Secret Access Key [****************siUL]:
37 | - Default region name [xx-xxxx-x]:
38 | - Default output format [None]:
39 |
40 | ## Credits
41 | - Andrew Sobottka - Linkedin | Github
42 | - Chris Bock - Linkedin | Github
43 | - David Tung - Linkedin | Github
44 | - Jade Majed BuGhanem - Linkedin | Github
45 | - Titan Tran - Linkedin | Github
46 |
--------------------------------------------------------------------------------
/src/app/components/calculations-updated.js:
--------------------------------------------------------------------------------
1 | const { default: ourData } = require("./data");
2 | import ourData from './data'
3 | import AWSdata from './data'
4 |
5 |
6 | const parseData = function (data) {
7 | const newData = [];
8 | for (let i = 0; i < data.length; i++) {
9 | const newEntry = {};
10 | for (let j = 0; j < data[i].length; j++) {
11 | newEntry[data[i][j].field] = data[i][j].value;
12 | }
13 | newData.push(newEntry);
14 | }
15 | return newData;
16 | };
17 |
18 | const readableMockData = parseData(AWSdata);
19 |
20 | const getInitInfo = function (data) {
21 | const results = [];
22 | for (let i = 0; i < data.length; i++) {
23 | const newEntry = {};
24 | if (data[i]['@initDuration']) {
25 | newEntry['timestamp'] = data[i]['@timestamp'];
26 | newEntry['initDuration'] = data[i]['@initDuration'];
27 | newEntry['warmInvocationsDuration'] = [];
28 | results.push(newEntry);
29 | } else if (data[i]['@duration'] && results.length !== 0)
30 | results[results.length - 1]['warmInvocationsDuration'].push(data[i]['@duration']);
31 | }
32 | return results;
33 | };
34 |
35 | const initInfo = getInitInfo(readableMockData);
36 |
37 | // ******************************************************************************************
38 |
39 |
40 | const endStartTraffic = (data) => {
41 | let start = [];
42 | let end = [];
43 | for (let i = data.length - 1; i >= 0; i--) {
44 | if (i === data.length - 1) {
45 | start.push(data[i].timestamp);
46 | } else if (data[i].day !== data[i + 1].day) {
47 | start.push(data[i].timestamp);
48 | }
49 |
50 | if (i === 0) {
51 | end.push(data[i].timestamp);
52 | } else if (data[i].day !== data[i - 1].day) {
53 | end.push(data[i].timestamp);
54 | }
55 | }
56 | return [start, end];
57 | };
58 |
59 | const [startTimestamps, endTimestamps] = endStartTraffic(ourData);
60 | //
61 | const averageTimestamp = (timestampsArray, prewarm) => {
62 | let results = [];
63 | let averageTime = '';
64 | let hoursArray = [];
65 | let minutesArray = [];
66 | let prewarmInput = 10 || prewarm;
67 | let prewarmTime = '';
68 | for (let i = 0; i < timestampsArray.length; i++) {
69 | let entry = timestampsArray[i].slice(timestampsArray[i].indexOf(' ') + 1, timestampsArray[i].indexOf('.') - 3);
70 | const hours = Number(entry.slice(0, entry.indexOf(':')));
71 | const minutes = Number(entry.slice(entry.indexOf(':') + 1));
72 | hoursArray.push(hours);
73 | minutesArray.push(minutes);
74 | }
75 | const hoursSum = hoursArray.reduce((a, b) => a + b, 0);
76 | const minutesSum = minutesArray.reduce((a, b) => a + b, 0);
77 | const totalMin = (hoursSum / hoursArray.length) * 60 + minutesSum / minutesArray.length;
78 | const preTotal = totalMin - prewarmInput;
79 | averageTime = Math.floor(totalMin / 60)
80 | .toString()
81 | .concat(':')
82 | .concat(Math.ceil(totalMin % 60).toString());
83 | prewarmTime = Math.floor(preTotal / 60)
84 | .toString()
85 | .concat(':')
86 | .concat(Math.ceil(preTotal % 60).toString());
87 | results.push(averageTime, prewarmTime);
88 | return results;
89 | };
90 |
91 | const averageStart = averageTimestamp(startTimestamps);
92 | const averageEnd = averageTimestamp(endTimestamps);
93 |
94 | module.exports = { parseData, getInitInfo };
95 |
--------------------------------------------------------------------------------
/src/app/components/email-template.tsx:
--------------------------------------------------------------------------------
1 | // src/app/components/email-template.tsx
2 | import * as React from 'react';
3 |
4 | interface EmailTemplateProps {
5 | Name: string;
6 | }
7 |
8 | export const EmailTemplate: React.FC> = ({
9 | Name,
10 | }) => {
11 | return (
12 |
13 |
Welcome, {Name}!
14 |
15 | Thanks for sending me an Email; here is your function data information!
16 |
17 |
18 |
25 |
26 |
27 | |
34 |
42 | Function name
43 | |
44 |
52 | Cold calls /week
53 | |
54 |
62 | Average cold start
63 | |
64 |
65 |
66 |
67 |
68 |
69 | |
76 | 1
77 | |
78 |
85 | ChrisTestFunc
86 | |
87 |
94 | 40
95 | |
96 |
103 | 180ms
104 | |
105 |
106 |
107 |
108 | |
115 | 2
116 | |
117 |
124 | ChrisPresentation
125 | |
126 |
133 | 30
134 | |
135 |
142 | 200ms
143 | |
144 |
145 |
146 |
147 |
148 |
149 | );
150 | };
151 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | /* eslint-disable max-len */
3 | module.exports = {
4 | content: [
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | "./node_modules/@tremor/**/*.{js,ts,jsx,tsx}",
7 | ],
8 | theme: {
9 | transparent: "transparent",
10 | current: "currentColor",
11 | extend: {
12 | fontFamily: {
13 | sans: ['Graphik', 'sans-serif'],
14 | serif: ['Merriweather', 'serif'],
15 | },
16 | colors: {
17 | // light mode
18 | tremor: {
19 | brand: {
20 | faint: "#eff6ff", // blue-50
21 | muted: "#bfdbfe", // blue-200
22 | subtle: "#60a5fa", // blue-400
23 | DEFAULT: "#3b82f6", // blue-500
24 | emphasis: "#1d4ed8", // blue-700
25 | inverted: "#ffffff", // white
26 | },
27 | background: {
28 | muted: "#f9fafb", // gray-50
29 | subtle: "#f3f4f6", // gray-100
30 | DEFAULT: "#ffffff", // white
31 | emphasis: "#374151", // gray-700
32 | },
33 | border: {
34 | DEFAULT: "#e5e7eb", // gray-200
35 | },
36 | ring: {
37 | DEFAULT: "#ffffff", //
38 | },
39 | content: {
40 | subtle: "#9ca3af", // gray-400
41 | DEFAULT: "#6b7280", // gray-500
42 | emphasis: "#374151", // gray-700
43 | strong: "#111827", // gray-900
44 | inverted: "#ffffff", // white
45 | },
46 | },
47 | // dark mode
48 | "dark-tremor": {
49 | brand: {
50 | faint: "#0B1229", // custom
51 | muted: "#172554", // blue-950
52 | subtle: "#1e40af", // blue-800
53 | DEFAULT: "#3b82f6", // blue-500
54 | emphasis: "#60a5fa", // blue-400
55 | inverted: "#030712", // gray-950
56 | },
57 | background: {
58 | muted: "#131A2B", // custom
59 | subtle: "#1f2937", // gray-800
60 | DEFAULT: "#111827", // gray-900
61 | emphasis: "#d1d5db", // gray-300
62 | },
63 | border: {
64 | DEFAULT: "#1f2937", // gray-800
65 | },
66 | ring: {
67 | DEFAULT: "#1f2937", // gray-800
68 | },
69 | content: {
70 | subtle: "#4b5563", // gray-600
71 | DEFAULT: "#6b7280", // gray-600
72 | emphasis: "#e5e7eb", // gray-200
73 | strong: "#f9fafb", // gray-50
74 | inverted: "#000000", // black
75 | },
76 | },
77 | },
78 | boxShadow: {
79 | // light
80 | "tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
81 | "tremor-card": "",
82 | "tremor-dropdown": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
83 | // dark
84 | "dark-tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
85 | "dark-tremor-card": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
86 | "dark-tremor-dropdown": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
87 | },
88 | borderRadius: {
89 | "tremor-small": "0.375rem",
90 | "tremor-default": "0rem",
91 | "tremor-full": "9999px",
92 | },
93 | fontSize: {
94 | "tremor-label": ["0.75rem"],
95 | "tremor-default": ["0.875rem", { lineHeight: "1.25rem" }],
96 | "tremor-title": ["1.25rem", { lineHeight: "1.25rem" }],
97 | "tremor-metric": ["1.3rem", { lineHeight: "1.4rem" }],
98 | }
99 | },
100 | },
101 | safelist: [
102 | {
103 | pattern:
104 | /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
105 | variants: ["hover", "ui-selected"],
106 | },
107 | {
108 | pattern:
109 | /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
110 | variants: ["hover", "ui-selected"],
111 | },
112 | {
113 | pattern:
114 | /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
115 | variants: ["hover", "ui-selected"],
116 | },
117 | {
118 | pattern:
119 | /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
120 | },
121 | {
122 | pattern:
123 | /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
124 | },
125 | {
126 | pattern:
127 | /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
128 | },
129 | ],
130 | plugins: [require("@headlessui/tailwindcss")],
131 | };
132 |
--------------------------------------------------------------------------------
/src/app/components/functionrow.tsx:
--------------------------------------------------------------------------------
1 | const AWS = require('aws-sdk');
2 | const dotenv = require('dotenv');
3 |
4 | import React from 'react';
5 | import Link from 'next/link';
6 | import Badges from './badges';
7 |
8 | //Do not confuse this for the Tremor component (not being imported) also called 'Button'. This import is our button component.
9 | import WarmButton from './WarmButton';
10 |
11 | import { parseData, getInitInfo } from './calculations-updated';
12 | import {
13 | Flex,
14 | Card,
15 | Title,
16 | Text,
17 | Metric,
18 | } from '@tremor/react';
19 | import allAccordionArray from './Accordions';
20 | import WarmPeriodTabs from './WarmPeriodTabs';
21 | import { allowedNodeEnvironmentFlags } from 'process';
22 | ``;
23 | import GetData from '../home/dataPage/page'
24 |
25 | //AWS Cloudwatch start and get query imports
26 | import { CloudWatchLogsClient, StartQueryCommand, GetQueryResultsCommand } from "@aws-sdk/client-cloudwatch-logs"; // ES Modules import
27 | import { LambdaClient, ListFunctionsCommand } from "@aws-sdk/client-lambda";
28 | import { Grey_Qo } from 'next/font/google';
29 |
30 | //___AWS_Start query: creates the query on AWS and returns response of queryId
31 | const startQueryFunc = async function(funcName) {
32 | const client = new CloudWatchLogsClient({region: 'us-east-2'});
33 | const oneWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // TODO: currently 24 hours ago, * 7 if want to change to week. unit is currently in seconds
34 | const now = new Date();
35 | const input = { // StartQueryRequest
36 | logGroupName: funcName,
37 | startTime: oneWeek.getTime(),
38 | endTime: now.getTime(),
39 | queryString:
40 | 'fields @ingestionTime, @initDuration, @logStream, @message, @timestamp, @type, @billedDuration, @duration, @maxMemoryUsed, @memorySize | sort @timestamp desc',
41 | limit: 1,
42 | };
43 | const command = new StartQueryCommand(input);
44 | const response = await client.send(command);
45 | return response;
46 | };
47 | //__AWS Get query: returns logs from a AWS query via a query ID
48 | const getQueryFunc = async function (queryId) {
49 | const client = new CloudWatchLogsClient(dotenv.config);
50 | const input = { // GetQueryResultsRequest
51 | queryId: queryId};
52 | const command = new GetQueryResultsCommand(input);
53 | const response = await client.send(command);
54 | return response; //response object has many properties, the results property is the array of logs we are interested in.
55 | }
56 | //calls startQuery and getQuery sequentially and does some data manipulation.
57 | const getLogs = async function( funcName ) {
58 | const QueryStartResults = await startQueryFunc(funcName);
59 | const queryId = QueryStartResults.queryId;
60 | const initialQueryGetResults = await getQueryFunc(queryId);
61 | setTimeout(async () => {
62 | let delayedQueryGetResults = await getQueryFunc(queryId);
63 | return delayedQueryGetResults.results
64 | }, 2000);
65 | //status goes from Scheduled, to Running, to Complete. or Failed/cancelled/timeout/unknown. see https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_GetQueryResults.html
66 | //setTimeout gives AWS some time to run the query.
67 | }
68 | //receives prop funcName and destructures it
69 | const functionrow = async ( { funcName , avgColdCalls , avgColdstartDur , dataIndex } ) => {
70 | let initInfo = [{
71 | timestamp: '2023-12-14 18:18:43.243',
72 | initDuration: '181.02',
73 | warmInvocationsDuration: ['11.04', '1.91', '2.36', '1.92', '1.36'],
74 | day: '2023-12-14',
75 | },
76 | {
77 | timestamp: '2023-12-14 18:18:43.243',
78 | initDuration: '181.02',
79 | warmInvocationsDuration: ['11.04', '1.91', '2.36', '1.92', '1.36'],
80 | day: '2023-12-14',
81 | }];
82 | const gotResults = await getLogs(funcName);
83 | // const initInfo = await getInitInfo(gotResults);
84 | const badges = [];
85 | const averageInitDuration = 200;
86 | //below var removes '/aws/lambda/' from the function name. eg '/aws/lambda/myLambda' ==> 'myLambda'
87 | const funcNameSliced = funcName.slice(12);
88 |
89 | return (
90 |
93 |
94 |
95 |
103 | {funcNameSliced}
104 |
105 |
106 | {/* */}
107 |
108 |
109 |
110 |
115 | {avgColdCalls}
116 | cold calls /week
117 |
118 |
123 | {avgColdstartDur}ms
124 | average cold start
125 |
126 |
127 |
128 |
129 |
130 | );
131 | };
132 |
133 | export default functionrow;
134 |
--------------------------------------------------------------------------------
/src/app/components/data.js:
--------------------------------------------------------------------------------
1 | // from 2023-12-14 to 2023-12-20 data
2 | const ourData = [
3 | {
4 | // ***************** 2023-12-20 BELOW ******************************
5 | timestamp: '2023-12-20 20:00:32.243',
6 | initDuration: '143.85',
7 | warmInvocationsDuration: ['1.99', '2.12', '2.86', '1.72'],
8 | day: '2023-12-20',
9 | },
10 | {
11 | timestamp: '2023-12-20 19:42:32.243',
12 | initDuration: '159.34',
13 | warmInvocationsDuration: ['1.78', '1.87', '2.01', '2.23'],
14 | day: '2023-12-20',
15 | },
16 | {
17 | timestamp: '2023-12-20 19:12:32.243',
18 | initDuration: '162.99',
19 | warmInvocationsDuration: ['2.04', '1.59', '2.21', '1.79', '1.34'],
20 | day: '2023-12-20',
21 | },
22 | {
23 | timestamp: '2023-12-20 18:25:25.243',
24 | initDuration: '188.92',
25 | warmInvocationsDuration: ['1.84', '1.70', '2.13', '1.97'],
26 | day: '2023-12-20',
27 | },
28 | {
29 | // ***************** 2023-12-19 BELOW ******************************
30 | timestamp: '2023-12-19 19:39:25.243',
31 | initDuration: '163.28',
32 | warmInvocationsDuration: ['2.02', '2.15', '2.89', '1.75'],
33 | day: '2023-12-19',
34 | },
35 | {
36 | timestamp: '2023-12-19 18:22:25.243',
37 | initDuration: '199.08',
38 | warmInvocationsDuration: ['1.80', '1.89', '2.25'],
39 | day: '2023-12-19',
40 | },
41 | {
42 | timestamp: '2023-12-19 17:59:25.243',
43 | initDuration: '172.37',
44 | warmInvocationsDuration: ['2.06', '1.61', '2.27', '1.83', '1.38', '1.99'],
45 | day: '2023-12-19',
46 | },
47 | {
48 | timestamp: '2023-12-19 17:45:25.243',
49 | initDuration: '198.34',
50 | warmInvocationsDuration: ['1.86', '1.72', '2.15'],
51 | day: '2023-12-19',
52 | },
53 | {
54 | timestamp: '2023-12-19 17:15:00.243',
55 | initDuration: '157.32',
56 | warmInvocationsDuration: ['2.39', '1.93', '1.59', '2.06'],
57 | day: '2023-12-19',
58 | },
59 | {
60 | timestamp: '2023-12-19 16:46:00.243',
61 | initDuration: '160.99',
62 | warmInvocationsDuration: ['1.49', '3.10', '2.01', '1.81', '1.26'],
63 | day: '2023-12-19',
64 | },
65 | {
66 | // ***************** 2023-12-18 BELOW ******************************
67 | timestamp: '2023-12-18 20:30:00.243',
68 | initDuration: '194.92',
69 | warmInvocationsDuration: ['1.97', '2.11', '2.85', '1.73'],
70 | day: '2023-12-18',
71 | },
72 | {
73 | timestamp: '2023-12-18 19:59:00.243',
74 | initDuration: '160.57',
75 | warmInvocationsDuration: ['1.82', '1.91', '2.05', '2.27'],
76 | day: '2023-12-18',
77 | },
78 | {
79 | timestamp: '2023-12-18 19:45:00.243',
80 | initDuration: '153.15',
81 | warmInvocationsDuration: ['2.08', '1.63', '2.29', '1.85', '1.40', '2.22'],
82 | day: '2023-12-18',
83 | },
84 | {
85 | timestamp: '2023-12-18 18:55:00.243',
86 | initDuration: '179.13',
87 | warmInvocationsDuration: ['1.88', '1.74', '2.17', '2.01'],
88 | day: '2023-12-18',
89 | },
90 | {
91 | timestamp: '2023-12-18 18:15:00.243',
92 | initDuration: '158.11',
93 | warmInvocationsDuration: ['1.61', '2.08'],
94 | day: '2023-12-18',
95 | },
96 | {
97 | timestamp: '2023-12-18 17:56:00.243',
98 | initDuration: '161.25',
99 | warmInvocationsDuration: ['1.51', '3.14', '2.05', '1.84', '1.28'],
100 | day: '2023-12-18',
101 | },
102 | {
103 | timestamp: '2023-12-18 17:37:04.243',
104 | initDuration: '166.83',
105 | warmInvocationsDuration: ['1.74', '1.98', '2.16', '1.61', '2.29'],
106 | day: '2023-12-18',
107 | },
108 | {
109 | // ***************** 2023-12-17 BELOW ******************************
110 | timestamp: '2023-12-17 19:32:04.243',
111 | initDuration: '174.68',
112 | warmInvocationsDuration: ['1.89', '2.21', '3.04', '1.78', '2.18'],
113 | day: '2023-12-17',
114 | },
115 | {
116 | timestamp: '2023-12-17 18:15:04.243',
117 | initDuration: '148.95',
118 | warmInvocationsDuration: ['2.45', '1.64', '2.12'],
119 | day: '2023-12-17',
120 | },
121 | {
122 | timestamp: '2023-12-17 17:46:19.243',
123 | initDuration: '159.42',
124 | warmInvocationsDuration: ['1.54', '3.22', '2.13', '1.91', '1.98', '1.34'],
125 | day: '2023-12-17',
126 | },
127 | {
128 | // ***************** 2023-12-16 BELOW ******************************
129 | timestamp: '2023-12-16 18:42:19.243',
130 | initDuration: '166.35',
131 | warmInvocationsDuration: ['1.24', '9.62', '2.45', '1.78', '3.19', '1.92'],
132 | day: '2023-12-16',
133 | },
134 | {
135 | timestamp: '2023-12-16 18:16:53.243',
136 | initDuration: '188.31',
137 | warmInvocationsDuration: ['1.54', '1.78', '2.13', '3.02'],
138 | day: '2023-12-16',
139 | },
140 | {
141 | timestamp: '2023-12-16 17:51:53.243',
142 | initDuration: '174.99',
143 | warmInvocationsDuration: ['2.35', '1.96', '1.45'],
144 | day: '2023-12-16',
145 | },
146 | {
147 | timestamp: '2023-12-16 17:33:53.243',
148 | initDuration: '158.29',
149 | warmInvocationsDuration: ['1.63', '3.18', '2.79', '1.94'],
150 | day: '2023-12-16',
151 | },
152 | {
153 | // ***************** 2023-12-15 BELOW ******************************
154 | timestamp: '2023-12-15 20:27:01.840',
155 | initDuration: '162.61',
156 | warmInvocationsDuration: ['1.34', '2.42', '1.83', '2.45', '1.42'],
157 | day: '2023-12-15',
158 | },
159 | {
160 | timestamp: '2023-12-15 19:43.243',
161 | initDuration: '145.02',
162 | warmInvocationsDuration: ['1.91', '2.36', '1.92', '1.36'],
163 | day: '2023-12-15',
164 | },
165 | {
166 | timestamp: '2023-12-15 19:16:43.243',
167 | initDuration: '122.01',
168 | warmInvocationsDuration: ['1.04', '1.71', '1.46', '2.82', '1.52', '1,24'],
169 | day: '2023-12-15',
170 | },
171 | {
172 | timestamp: '2023-12-15 18:47:43.243',
173 | initDuration: '152.75',
174 | warmInvocationsDuration: ['3.31', '1.56', '2.68'],
175 | day: '2023-12-15',
176 | }, // ***************** 2023-12-14 BELOW ******************************
177 | {
178 | timestamp: '2023-12-14 19:42:01.840',
179 | initDuration: '165.61',
180 | warmInvocationsDuration: ['1.34', '12.42'],
181 | day: '2023-12-14',
182 | },
183 | {
184 | timestamp: '2023-12-14 18:18:43.243',
185 | initDuration: '181.02',
186 | warmInvocationsDuration: ['11.04', '1.91', '2.36', '1.92', '1.36'],
187 | day: '2023-12-14',
188 | },
189 | {
190 | timestamp: '2023-12-14 17:46:43.243',
191 | initDuration: '142.05',
192 | warmInvocationsDuration: ['1.04', '1.71', '1.46'],
193 | day: '2023-12-14',
194 | },
195 | {
196 | timestamp: '2023-12-14 17:21:43.243',
197 | initDuration: '192.01',
198 | warmInvocationsDuration: ['9.04', '1.71', '2.33', '1.95', '1.46', '2.18'],
199 | day: '2023-12-14',
200 | },
201 | ];
202 |
203 | const AWSdata = [
204 | [
205 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
206 | { field: '@initDuration', value: '103.01' },
207 | {
208 | field: '@logStream',
209 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
210 | },
211 | {
212 | field: '@message',
213 | value: 'END RequestId: 76269889-7d50-4d6d-a3f1-9b4f5322f47c\n',
214 | },
215 | { field: '@timestamp', value: '2023-12-05 22:14:43.877' },
216 | { field: '@type', value: 'END' },
217 | {
218 | field: '@ptr',
219 | value:
220 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAIYAQ==',
221 | },
222 | ],
223 | ];
224 |
225 | export default ourData ;
--------------------------------------------------------------------------------
/src/app/retrievedData.js:
--------------------------------------------------------------------------------
1 | const mockData = [
2 | [
3 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
4 | {
5 | field: '@logStream',
6 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
7 | },
8 | {
9 | field: '@message',
10 | value: 'END RequestId: 76269889-7d50-4d6d-a3f1-9b4f5322f47c\n',
11 | },
12 | { field: '@timestamp', value: '2023-12-05 22:14:43.877' },
13 | { field: '@type', value: 'END' },
14 | {
15 | field: '@ptr',
16 | value:
17 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAIYAQ==',
18 | },
19 | ],
20 | [
21 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
22 | {
23 | field: '@logStream',
24 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
25 | },
26 | {
27 | field: '@message',
28 | value:
29 | 'REPORT RequestId: 76269889-7d50-4d6d-a3f1-9b4f5322f47c\tDuration: 14.97 ms\tBilled Duration: 15 ms\tMemory Size: 128 MB\tMax Memory Used: 67 MB\t\n',
30 | },
31 | { field: '@timestamp', value: '2023-12-05 22:14:43.877' },
32 | { field: '@type', value: 'REPORT' },
33 | { field: '@billedDuration', value: '15.0' },
34 | { field: '@duration', value: '14.97' },
35 | { field: '@maxMemoryUsed', value: '6.7E7' },
36 | { field: '@memorySize', value: '1.28E8' },
37 | {
38 | field: '@ptr',
39 | value:
40 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAMYAQ==',
41 | },
42 | ],
43 | [
44 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
45 | {
46 | field: '@logStream',
47 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
48 | },
49 | {
50 | field: '@message',
51 | value:
52 | '2023-12-05T22:14:43.862Z\t76269889-7d50-4d6d-a3f1-9b4f5322f47c\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
53 | },
54 | { field: '@timestamp', value: '2023-12-05 22:14:43.862' },
55 | {
56 | field: '@ptr',
57 | value:
58 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAAYAQ==',
59 | },
60 | ],
61 | [
62 | { field: '@ingestionTime', value: '2023-12-05 22:14:52.883' },
63 | {
64 | field: '@logStream',
65 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
66 | },
67 | {
68 | field: '@message',
69 | value:
70 | 'START RequestId: 76269889-7d50-4d6d-a3f1-9b4f5322f47c Version: $LATEST\n',
71 | },
72 | { field: '@timestamp', value: '2023-12-05 22:14:43.862' },
73 | { field: '@type', value: 'START' },
74 | {
75 | field: '@ptr',
76 | value:
77 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAALF88qQAGVvoIwAAAAkIgASiW97DgwzEwpfew4MMxOARAkgRIxgxQ+AYYACABEAEYAQ==',
78 | },
79 | ],
80 | [
81 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
82 | { field: '@initDuration', value: '145.69' },
83 | {
84 | field: '@logStream',
85 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
86 | },
87 | {
88 | field: '@message',
89 | value:
90 | 'REPORT RequestId: be5b175d-ff9d-40db-adef-77555f4b77a8\tDuration: 5.93 ms\tBilled Duration: 6 ms\tMemory Size: 128 MB\tMax Memory Used: 67 MB\tInit Duration: 145.69 ms\t\n',
91 | },
92 | { field: '@timestamp', value: '2023-12-05 22:10:47.479' },
93 | { field: '@type', value: 'REPORT' },
94 | { field: '@billedDuration', value: '6.0' },
95 | { field: '@duration', value: '5.93' },
96 | { field: '@maxMemoryUsed', value: '6.7E7' },
97 | { field: '@memorySize', value: '1.28E8' },
98 | {
99 | field: '@ptr',
100 | value:
101 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAUYAQ==',
102 | },
103 | ],
104 | [
105 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
106 | {
107 | field: '@logStream',
108 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
109 | },
110 | {
111 | field: '@message',
112 | value: 'END RequestId: be5b175d-ff9d-40db-adef-77555f4b77a8\n',
113 | },
114 | { field: '@timestamp', value: '2023-12-05 22:10:47.478' },
115 | { field: '@type', value: 'END' },
116 | {
117 | field: '@ptr',
118 | value:
119 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAQYAQ==',
120 | },
121 | ],
122 | [
123 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
124 | {
125 | field: '@logStream',
126 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
127 | },
128 | {
129 | field: '@message',
130 | value:
131 | '2023-12-05T22:10:47.472Z\tbe5b175d-ff9d-40db-adef-77555f4b77a8\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
132 | },
133 | { field: '@timestamp', value: '2023-12-05 22:10:47.472' },
134 | {
135 | field: '@ptr',
136 | value:
137 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAMYAQ==',
138 | },
139 | ],
140 | [
141 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
142 | {
143 | field: '@logStream',
144 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
145 | },
146 | {
147 | field: '@message',
148 | value:
149 | 'START RequestId: be5b175d-ff9d-40db-adef-77555f4b77a8 Version: $LATEST\n',
150 | },
151 | { field: '@timestamp', value: '2023-12-05 22:10:47.471' },
152 | { field: '@type', value: 'START' },
153 | {
154 | field: '@ptr',
155 | value:
156 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAIYAQ==',
157 | },
158 | ],
159 | [
160 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
161 | {
162 | field: '@logStream',
163 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
164 | },
165 | {
166 | field: '@message',
167 | value: '2023-12-05T22:10:47.468Z\tundefined\tINFO\tLoading function\n',
168 | },
169 | { field: '@timestamp', value: '2023-12-05 22:10:47.468' },
170 | {
171 | field: '@ptr',
172 | value:
173 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAEYAQ==',
174 | },
175 | ],
176 | [
177 | { field: '@ingestionTime', value: '2023-12-05 22:10:51.653' },
178 | {
179 | field: '@logStream',
180 | value: '2023/12/05/[$LATEST]e34ea4d32b8f441c96246bbee30ff465',
181 | },
182 | {
183 | field: '@message',
184 | value:
185 | 'INIT_START Runtime Version: nodejs:18.v18\tRuntime Version ARN: arn:aws:lambda:us-east-2::runtime:d949ec0248c1b8fbc8cbc6df1c986e05fb933506b51d53be82d46f9a37125bea\n',
186 | },
187 | { field: '@timestamp', value: '2023-12-05 22:10:47.324' },
188 | {
189 | field: '@ptr',
190 | value:
191 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQARI1GhgCBlIsRIMAAAABW3gKsAAGVvn9wAAAAzIgASicv6LgwzEwt8Ci4MMxOAZAwgZI4A5Q3wgYACABEAAYAQ==',
192 | },
193 | ],
194 | [
195 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
196 | {
197 | field: '@logStream',
198 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
199 | },
200 | {
201 | field: '@message',
202 | value: 'END RequestId: 94376af3-22fc-4de1-bc5c-7df1e71766f9\n',
203 | },
204 | { field: '@timestamp', value: '2023-12-04 21:47:03.606' },
205 | { field: '@type', value: 'END' },
206 | {
207 | field: '@ptr',
208 | value:
209 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBYYAQ==',
210 | },
211 | ],
212 | [
213 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
214 | {
215 | field: '@logStream',
216 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
217 | },
218 | {
219 | field: '@message',
220 | value:
221 | 'REPORT RequestId: 94376af3-22fc-4de1-bc5c-7df1e71766f9\tDuration: 1.49 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
222 | },
223 | { field: '@timestamp', value: '2023-12-04 21:47:03.606' },
224 | { field: '@type', value: 'REPORT' },
225 | { field: '@billedDuration', value: '2.0' },
226 | { field: '@duration', value: '1.49' },
227 | { field: '@maxMemoryUsed', value: '6.8E7' },
228 | { field: '@memorySize', value: '1.28E8' },
229 | {
230 | field: '@ptr',
231 | value:
232 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBcYAQ==',
233 | },
234 | ],
235 | [
236 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
237 | {
238 | field: '@logStream',
239 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
240 | },
241 | {
242 | field: '@message',
243 | value:
244 | '2023-12-04T21:47:03.604Z\t94376af3-22fc-4de1-bc5c-7df1e71766f9\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
245 | },
246 | { field: '@timestamp', value: '2023-12-04 21:47:03.604' },
247 | {
248 | field: '@ptr',
249 | value:
250 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBQYAQ==',
251 | },
252 | ],
253 | [
254 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
255 | {
256 | field: '@logStream',
257 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
258 | },
259 | {
260 | field: '@message',
261 | value:
262 | 'START RequestId: 94376af3-22fc-4de1-bc5c-7df1e71766f9 Version: $LATEST\n',
263 | },
264 | { field: '@timestamp', value: '2023-12-04 21:47:03.604' },
265 | { field: '@type', value: 'START' },
266 | {
267 | field: '@ptr',
268 | value:
269 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBUYAQ==',
270 | },
271 | ],
272 | [
273 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
274 | {
275 | field: '@logStream',
276 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
277 | },
278 | {
279 | field: '@message',
280 | value: 'END RequestId: 5486ce42-96dc-4c6c-8424-21c1880ca262\n',
281 | },
282 | { field: '@timestamp', value: '2023-12-04 21:47:02.590' },
283 | { field: '@type', value: 'END' },
284 | {
285 | field: '@ptr',
286 | value:
287 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBIYAQ==',
288 | },
289 | ],
290 | [
291 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
292 | {
293 | field: '@logStream',
294 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
295 | },
296 | {
297 | field: '@message',
298 | value:
299 | 'REPORT RequestId: 5486ce42-96dc-4c6c-8424-21c1880ca262\tDuration: 1.44 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
300 | },
301 | { field: '@timestamp', value: '2023-12-04 21:47:02.590' },
302 | { field: '@type', value: 'REPORT' },
303 | { field: '@billedDuration', value: '2.0' },
304 | { field: '@duration', value: '1.44' },
305 | { field: '@maxMemoryUsed', value: '6.8E7' },
306 | { field: '@memorySize', value: '1.28E8' },
307 | {
308 | field: '@ptr',
309 | value:
310 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBMYAQ==',
311 | },
312 | ],
313 | [
314 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
315 | {
316 | field: '@logStream',
317 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
318 | },
319 | {
320 | field: '@message',
321 | value:
322 | '2023-12-04T21:47:02.588Z\t5486ce42-96dc-4c6c-8424-21c1880ca262\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
323 | },
324 | { field: '@timestamp', value: '2023-12-04 21:47:02.588' },
325 | {
326 | field: '@ptr',
327 | value:
328 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBAYAQ==',
329 | },
330 | ],
331 | [
332 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
333 | {
334 | field: '@logStream',
335 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
336 | },
337 | {
338 | field: '@message',
339 | value:
340 | 'START RequestId: 5486ce42-96dc-4c6c-8424-21c1880ca262 Version: $LATEST\n',
341 | },
342 | { field: '@timestamp', value: '2023-12-04 21:47:02.588' },
343 | { field: '@type', value: 'START' },
344 | {
345 | field: '@ptr',
346 | value:
347 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEBEYAQ==',
348 | },
349 | ],
350 | [
351 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
352 | {
353 | field: '@logStream',
354 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
355 | },
356 | {
357 | field: '@message',
358 | value: 'END RequestId: 2709a79e-0740-491b-87cf-08f9c1e4089b\n',
359 | },
360 | { field: '@timestamp', value: '2023-12-04 21:47:01.842' },
361 | { field: '@type', value: 'END' },
362 | {
363 | field: '@ptr',
364 | value:
365 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEA4YAQ==',
366 | },
367 | ],
368 | [
369 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
370 | {
371 | field: '@logStream',
372 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
373 | },
374 | {
375 | field: '@message',
376 | value:
377 | 'REPORT RequestId: 2709a79e-0740-491b-87cf-08f9c1e4089b\tDuration: 1.85 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
378 | },
379 | { field: '@timestamp', value: '2023-12-04 21:47:01.842' },
380 | { field: '@type', value: 'REPORT' },
381 | { field: '@billedDuration', value: '2.0' },
382 | { field: '@duration', value: '1.85' },
383 | { field: '@maxMemoryUsed', value: '6.8E7' },
384 | { field: '@memorySize', value: '1.28E8' },
385 | {
386 | field: '@ptr',
387 | value:
388 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEA8YAQ==',
389 | },
390 | ],
391 | [
392 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
393 | { field: '@initDuration', value: '162.61' },
394 | {
395 | field: '@logStream',
396 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
397 | },
398 | {
399 | field: '@message',
400 | value:
401 | '2023-12-04T21:47:01.840Z\t2709a79e-0740-491b-87cf-08f9c1e4089b\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
402 | },
403 | { field: '@timestamp', value: '2023-12-04 21:47:01.840' },
404 | {
405 | field: '@ptr',
406 | value:
407 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAwYAQ==',
408 | },
409 | ],
410 | [
411 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
412 | {
413 | field: '@logStream',
414 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
415 | },
416 | {
417 | field: '@message',
418 | value:
419 | 'START RequestId: 2709a79e-0740-491b-87cf-08f9c1e4089b Version: $LATEST\n',
420 | },
421 | { field: '@timestamp', value: '2023-12-04 21:47:01.840' },
422 | { field: '@type', value: 'START' },
423 | {
424 | field: '@ptr',
425 | value:
426 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEA0YAQ==',
427 | },
428 | ],
429 | [
430 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
431 | {
432 | field: '@logStream',
433 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
434 | },
435 | {
436 | field: '@message',
437 | value: 'END RequestId: 86c17def-3125-4c87-9761-8b5644e7cd6b\n',
438 | },
439 | { field: '@timestamp', value: '2023-12-04 21:47:01.188' },
440 | { field: '@type', value: 'END' },
441 | {
442 | field: '@ptr',
443 | value:
444 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAoYAQ==',
445 | },
446 | ],
447 | [
448 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
449 | {
450 | field: '@logStream',
451 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
452 | },
453 | {
454 | field: '@message',
455 | value:
456 | 'REPORT RequestId: 86c17def-3125-4c87-9761-8b5644e7cd6b\tDuration: 1.34 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
457 | },
458 | { field: '@timestamp', value: '2023-12-04 21:47:01.188' },
459 | { field: '@type', value: 'REPORT' },
460 | { field: '@billedDuration', value: '2.0' },
461 | { field: '@duration', value: '1.34' },
462 | { field: '@maxMemoryUsed', value: '6.8E7' },
463 | { field: '@memorySize', value: '1.28E8' },
464 | {
465 | field: '@ptr',
466 | value:
467 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAsYAQ==',
468 | },
469 | ],
470 | [
471 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
472 | {
473 | field: '@logStream',
474 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
475 | },
476 | {
477 | field: '@message',
478 | value:
479 | '2023-12-04T21:47:01.186Z\t86c17def-3125-4c87-9761-8b5644e7cd6b\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
480 | },
481 | { field: '@timestamp', value: '2023-12-04 21:47:01.186' },
482 | {
483 | field: '@ptr',
484 | value:
485 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAgYAQ==',
486 | },
487 | ],
488 | [
489 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
490 | {
491 | field: '@logStream',
492 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
493 | },
494 | {
495 | field: '@message',
496 | value:
497 | 'START RequestId: 86c17def-3125-4c87-9761-8b5644e7cd6b Version: $LATEST\n',
498 | },
499 | { field: '@timestamp', value: '2023-12-04 21:47:01.186' },
500 | { field: '@type', value: 'START' },
501 | {
502 | field: '@ptr',
503 | value:
504 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAkYAQ==',
505 | },
506 | ],
507 | [
508 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
509 | {
510 | field: '@logStream',
511 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
512 | },
513 | {
514 | field: '@message',
515 | value: 'END RequestId: dfa827df-e076-4bfe-a610-dda1b699c6b0\n',
516 | },
517 | { field: '@timestamp', value: '2023-12-04 21:47:00.444' },
518 | { field: '@type', value: 'END' },
519 | {
520 | field: '@ptr',
521 | value:
522 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAYYAQ==',
523 | },
524 | ],
525 | [
526 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
527 | {
528 | field: '@logStream',
529 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
530 | },
531 | {
532 | field: '@message',
533 | value:
534 | 'REPORT RequestId: dfa827df-e076-4bfe-a610-dda1b699c6b0\tDuration: 31.42 ms\tBilled Duration: 32 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
535 | },
536 | { field: '@timestamp', value: '2023-12-04 21:47:00.444' },
537 | { field: '@type', value: 'REPORT' },
538 | { field: '@billedDuration', value: '32.0' },
539 | { field: '@duration', value: '31.42' },
540 | { field: '@maxMemoryUsed', value: '6.8E7' },
541 | { field: '@memorySize', value: '1.28E8' },
542 | {
543 | field: '@ptr',
544 | value:
545 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAcYAQ==',
546 | },
547 | ],
548 | [
549 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
550 | {
551 | field: '@logStream',
552 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
553 | },
554 | {
555 | field: '@message',
556 | value:
557 | '2023-12-04T21:47:00.422Z\tdfa827df-e076-4bfe-a610-dda1b699c6b0\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
558 | },
559 | { field: '@timestamp', value: '2023-12-04 21:47:00.422' },
560 | {
561 | field: '@ptr',
562 | value:
563 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAUYAQ==',
564 | },
565 | ],
566 | [
567 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
568 | {
569 | field: '@logStream',
570 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
571 | },
572 | {
573 | field: '@message',
574 | value:
575 | 'START RequestId: dfa827df-e076-4bfe-a610-dda1b699c6b0 Version: $LATEST\n',
576 | },
577 | { field: '@timestamp', value: '2023-12-04 21:47:00.412' },
578 | { field: '@type', value: 'START' },
579 | {
580 | field: '@ptr',
581 | value:
582 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAQYAQ==',
583 | },
584 | ],
585 | [
586 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
587 | { field: '@initDuration', value: '192.01' },
588 | {
589 | field: '@logStream',
590 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
591 | },
592 | {
593 | field: '@message',
594 | value: 'END RequestId: 5ab88447-c672-490f-8220-bd5374e3d415\n',
595 | },
596 | { field: '@timestamp', value: '2023-12-04 21:46:57.243' },
597 | { field: '@type', value: 'END' },
598 | {
599 | field: '@ptr',
600 | value:
601 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAIYAQ==',
602 | },
603 | ],
604 | [
605 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
606 | {
607 | field: '@logStream',
608 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
609 | },
610 | {
611 | field: '@message',
612 | value:
613 | 'REPORT RequestId: 5ab88447-c672-490f-8220-bd5374e3d415\tDuration: 118.04 ms\tBilled Duration: 119 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
614 | },
615 | { field: '@timestamp', value: '2023-12-04 21:46:57.243' },
616 | { field: '@type', value: 'REPORT' },
617 | { field: '@billedDuration', value: '119.0' },
618 | { field: '@duration', value: '118.04' },
619 | { field: '@maxMemoryUsed', value: '6.8E7' },
620 | { field: '@memorySize', value: '1.28E8' },
621 | {
622 | field: '@ptr',
623 | value:
624 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAMYAQ==',
625 | },
626 | ],
627 | [
628 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
629 | {
630 | field: '@logStream',
631 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
632 | },
633 | {
634 | field: '@message',
635 | value:
636 | '2023-12-04T21:46:57.222Z\t5ab88447-c672-490f-8220-bd5374e3d415\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
637 | },
638 | { field: '@timestamp', value: '2023-12-04 21:46:57.222' },
639 | {
640 | field: '@ptr',
641 | value:
642 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAEYAQ==',
643 | },
644 | ],
645 | [
646 | { field: '@ingestionTime', value: '2023-12-04 21:47:06.148' },
647 | {
648 | field: '@logStream',
649 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
650 | },
651 | {
652 | field: '@message',
653 | value:
654 | 'START RequestId: 5ab88447-c672-490f-8220-bd5374e3d415 Version: $LATEST\n',
655 | },
656 | { field: '@timestamp', value: '2023-12-04 21:46:57.125' },
657 | { field: '@type', value: 'START' },
658 | {
659 | field: '@ptr',
660 | value:
661 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBhI1GhgCBkoEqU8AAAADle8gRwAGVuSNYAAAAPIgASjl4bG2wzEwtpSytsMxOBhA5hhI8hBQowsYACABEAAYAQ==',
662 | },
663 | ],
664 | [
665 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
666 | {
667 | field: '@logStream',
668 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
669 | },
670 | {
671 | field: '@message',
672 | value:
673 | 'REPORT RequestId: ae744449-4ff4-47d7-8fdd-beb8d0f71eff\tDuration: 1.71 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
674 | },
675 | { field: '@timestamp', value: '2023-12-04 21:44:58.423' },
676 | { field: '@type', value: 'REPORT' },
677 | { field: '@billedDuration', value: '2.0' },
678 | { field: '@duration', value: '1.71' },
679 | { field: '@maxMemoryUsed', value: '6.8E7' },
680 | { field: '@memorySize', value: '1.28E8' },
681 | {
682 | field: '@ptr',
683 | value:
684 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEA8YAQ==',
685 | },
686 | ],
687 | [
688 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
689 | {
690 | field: '@logStream',
691 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
692 | },
693 | {
694 | field: '@message',
695 | value: 'END RequestId: ae744449-4ff4-47d7-8fdd-beb8d0f71eff\n',
696 | },
697 | { field: '@timestamp', value: '2023-12-04 21:44:58.423' },
698 | { field: '@type', value: 'END' },
699 | {
700 | field: '@ptr',
701 | value:
702 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEA4YAQ==',
703 | },
704 | ],
705 | [
706 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
707 | {
708 | field: '@logStream',
709 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
710 | },
711 | {
712 | field: '@message',
713 | value:
714 | '2023-12-04T21:44:58.419Z\tae744449-4ff4-47d7-8fdd-beb8d0f71eff\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
715 | },
716 | { field: '@timestamp', value: '2023-12-04 21:44:58.419' },
717 | {
718 | field: '@ptr',
719 | value:
720 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEA0YAQ==',
721 | },
722 | ],
723 | [
724 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
725 | {
726 | field: '@logStream',
727 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
728 | },
729 | {
730 | field: '@message',
731 | value:
732 | 'START RequestId: ae744449-4ff4-47d7-8fdd-beb8d0f71eff Version: $LATEST\n',
733 | },
734 | { field: '@timestamp', value: '2023-12-04 21:44:58.418' },
735 | { field: '@type', value: 'START' },
736 | {
737 | field: '@ptr',
738 | value:
739 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAwYAQ==',
740 | },
741 | ],
742 | [
743 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
744 | {
745 | field: '@logStream',
746 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
747 | },
748 | {
749 | field: '@message',
750 | value:
751 | 'REPORT RequestId: 9b205d31-ac80-4b06-9193-f81ee784171c\tDuration: 2.33 ms\tBilled Duration: 3 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
752 | },
753 | { field: '@timestamp', value: '2023-12-04 21:44:56.343' },
754 | { field: '@type', value: 'REPORT' },
755 | { field: '@billedDuration', value: '3.0' },
756 | { field: '@duration', value: '2.33' },
757 | { field: '@maxMemoryUsed', value: '6.8E7' },
758 | { field: '@memorySize', value: '1.28E8' },
759 | {
760 | field: '@ptr',
761 | value:
762 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAsYAQ==',
763 | },
764 | ],
765 | [
766 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
767 | {
768 | field: '@logStream',
769 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
770 | },
771 | {
772 | field: '@message',
773 | value: 'END RequestId: 9b205d31-ac80-4b06-9193-f81ee784171c\n',
774 | },
775 | { field: '@timestamp', value: '2023-12-04 21:44:56.343' },
776 | { field: '@type', value: 'END' },
777 | {
778 | field: '@ptr',
779 | value:
780 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAoYAQ==',
781 | },
782 | ],
783 | [
784 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
785 | {
786 | field: '@logStream',
787 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
788 | },
789 | {
790 | field: '@message',
791 | value:
792 | 'START RequestId: 9b205d31-ac80-4b06-9193-f81ee784171c Version: $LATEST\n',
793 | },
794 | { field: '@timestamp', value: '2023-12-04 21:44:56.331' },
795 | { field: '@type', value: 'START' },
796 | {
797 | field: '@ptr',
798 | value:
799 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAkYAQ==',
800 | },
801 | ],
802 | [
803 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
804 | {
805 | field: '@logStream',
806 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
807 | },
808 | {
809 | field: '@message',
810 | value:
811 | '2023-12-04T21:44:56.331Z\t9b205d31-ac80-4b06-9193-f81ee784171c\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
812 | },
813 | { field: '@timestamp', value: '2023-12-04 21:44:56.331' },
814 | {
815 | field: '@ptr',
816 | value:
817 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAgYAQ==',
818 | },
819 | ],
820 | [
821 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
822 | {
823 | field: '@logStream',
824 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
825 | },
826 | {
827 | field: '@message',
828 | value: 'END RequestId: 6ebfcccd-ad0d-4378-b89a-1e3dcbce8db6\n',
829 | },
830 | { field: '@timestamp', value: '2023-12-04 21:44:54.748' },
831 | { field: '@type', value: 'END' },
832 | {
833 | field: '@ptr',
834 | value:
835 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAYYAQ==',
836 | },
837 | ],
838 | [
839 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
840 | {
841 | field: '@logStream',
842 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
843 | },
844 | {
845 | field: '@message',
846 | value:
847 | 'REPORT RequestId: 6ebfcccd-ad0d-4378-b89a-1e3dcbce8db6\tDuration: 1.95 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
848 | },
849 | { field: '@timestamp', value: '2023-12-04 21:44:54.748' },
850 | { field: '@type', value: 'REPORT' },
851 | { field: '@billedDuration', value: '2.0' },
852 | { field: '@duration', value: '1.95' },
853 | { field: '@maxMemoryUsed', value: '6.8E7' },
854 | { field: '@memorySize', value: '1.28E8' },
855 | {
856 | field: '@ptr',
857 | value:
858 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAcYAQ==',
859 | },
860 | ],
861 | [
862 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
863 | {
864 | field: '@logStream',
865 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
866 | },
867 | {
868 | field: '@message',
869 | value:
870 | '2023-12-04T21:44:54.746Z\t6ebfcccd-ad0d-4378-b89a-1e3dcbce8db6\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
871 | },
872 | { field: '@timestamp', value: '2023-12-04 21:44:54.746' },
873 | {
874 | field: '@ptr',
875 | value:
876 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAUYAQ==',
877 | },
878 | ],
879 | [
880 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
881 | {
882 | field: '@logStream',
883 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
884 | },
885 | {
886 | field: '@message',
887 | value:
888 | 'START RequestId: 6ebfcccd-ad0d-4378-b89a-1e3dcbce8db6 Version: $LATEST\n',
889 | },
890 | { field: '@timestamp', value: '2023-12-04 21:44:54.745' },
891 | { field: '@type', value: 'START' },
892 | {
893 | field: '@ptr',
894 | value:
895 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAQYAQ==',
896 | },
897 | ],
898 | [
899 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
900 | {
901 | field: '@logStream',
902 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
903 | },
904 | {
905 | field: '@message',
906 | value: 'END RequestId: 7b3b1e5f-b90b-44d3-99b3-48963ec65c7b\n',
907 | },
908 | { field: '@timestamp', value: '2023-12-04 21:44:52.383' },
909 | { field: '@type', value: 'END' },
910 | {
911 | field: '@ptr',
912 | value:
913 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAIYAQ==',
914 | },
915 | ],
916 | [
917 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
918 | {
919 | field: '@logStream',
920 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
921 | },
922 | {
923 | field: '@message',
924 | value:
925 | 'REPORT RequestId: 7b3b1e5f-b90b-44d3-99b3-48963ec65c7b\tDuration: 1.46 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 68 MB\t\n',
926 | },
927 | { field: '@timestamp', value: '2023-12-04 21:44:52.383' },
928 | { field: '@type', value: 'REPORT' },
929 | { field: '@billedDuration', value: '2.0' },
930 | { field: '@duration', value: '1.46' },
931 | { field: '@maxMemoryUsed', value: '6.8E7' },
932 | { field: '@memorySize', value: '1.28E8' },
933 | {
934 | field: '@ptr',
935 | value:
936 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAMYAQ==',
937 | },
938 | ],
939 | [
940 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
941 | {
942 | field: '@logStream',
943 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
944 | },
945 | {
946 | field: '@message',
947 | value:
948 | '2023-12-04T21:44:52.381Z\t7b3b1e5f-b90b-44d3-99b3-48963ec65c7b\tINFO\t a picture just got uploaded to the S3 original-dinner-time-images bucket!\n',
949 | },
950 | { field: '@timestamp', value: '2023-12-04 21:44:52.381' },
951 | {
952 | field: '@ptr',
953 | value:
954 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAAYAQ==',
955 | },
956 | ],
957 | [
958 | { field: '@ingestionTime', value: '2023-12-04 21:45:01.417' },
959 | {
960 | field: '@logStream',
961 | value: '2023/12/04/[$LATEST]c8a940ee198b466b8710087fdf685ba8',
962 | },
963 | {
964 | field: '@message',
965 | value:
966 | 'START RequestId: 7b3b1e5f-b90b-44d3-99b3-48963ec65c7b Version: $LATEST\n',
967 | },
968 | { field: '@timestamp', value: '2023-12-04 21:44:52.381' },
969 | { field: '@type', value: 'START' },
970 | {
971 | field: '@ptr',
972 | value:
973 | 'CnAKMwovMzQ3MTI5MzM4MjQ4Oi9hd3MvbGFtYmRhL3RpdGFucy1sYW1iZGEtbG9nLXRlc3QQBBI1GhgCBladNOQAAAAAIhwV8wAGVuSBQAAAAkIgASidk6q2wzEwt8KqtsMxOBBAwBBItw9Q6QkYACABEAEYAQ==',
974 | },
975 | ],
976 | ];
977 |
978 | // const queryString = 'fields @timestamp, @initDuration, @message, @ingestionTime, @type, @duration, @logStream, @maxMemoryUsed, @memorySize | sort @timestamp desc| limit 200';
979 | //
980 |
981 | // *************** Task 1: Getting the name of the function ***************
982 | // the name of our s3 bucket is called original-dinner-time-images, the name of our function is called titans-lambda-log-function. this is obtainable through the variable params.logGroupName on server.js and is not information we query for on AWS CloudWatch
983 | //
984 | // *************** Task 2: Average duration of warm time / 24 hour scale ***************
985 | // we will need to take the limit parameter out of the queryString and get all the logs from the past week but in this case of (limit 200 logs, this can represent our whole week)
986 | // my approach: there is no way to tell from the logs that a lambda function is cooled down, we can only tell if it has warmed it with the @initDuration property. after doing some research, a lambda function stays warm for 5-15 minutes from its last call. we can use 9 minutes as the average time of cool (not 10 because it makes our data always a noticable multiple of 10). for the function below, i will find the number of @initDuration invocations and multiply it by 9 minutes. which shouldnt be alot at all considering we havent tested this lambda-function a lot
987 | // for our dinner-time companion app: i would say that 5-7:30pm are prime dinner hours so our mockData calculation would be approximately 159 minutes/ 1440 minutes in a day. so approximately 11% of the day.
988 | //
989 | const averageWarmDuration = function (mockData) {
990 | const initDurationArray = [];
991 | let initCount = 0;
992 | let betweenCount = 0;
993 |
994 | for (let i = mockData.length - 1; i > 0; i--) {
995 | let timestamp = '';
996 | let initDuration = '';
997 | for (let j = 0; j < mockData[i].length; j++) {
998 | if (mockData[i][j].field === '@initDuration') {
999 | // console.log('hi');
1000 | // Loop through the array again because we do not know where @timestamp is relative to @initDuration
1001 | for (let k = 0; k < mockData[i].length; k++) {
1002 | if (mockData[i][k].field === '@timestamp') {
1003 | timestamp = mockData[i][k].value;
1004 | initDuration = mockData[i][j].value;
1005 | initCount++;
1006 |
1007 | // look thorugh every fieldObject now and check to see if it was a warmRun and push it onto the warmInvocations[];
1008 | // field: 'message', value: 'START...'
1009 | const entry = {
1010 | initNumber: initCount,
1011 | timestamp: timestamp,
1012 | initDuration: initDuration,
1013 | // warmInvocations: betweenCount,
1014 | };
1015 |
1016 | initDurationArray.push(entry);
1017 | break; // stop loop once @timestamp is found
1018 | }
1019 | }
1020 | }
1021 | // after we have an entry, we can access it with the initNumber - 1 like entry[initNumber] - 1;
1022 | if (mockData[i][j].value === 'START') betweenCount++;
1023 | }
1024 | }
1025 | return initDurationArray;
1026 | };
1027 |
1028 | const initDurationsArray = averageWarmDuration(mockData);
1029 | console.log(
1030 | 'The average time of our lambda function being warm per day (minutes): ',
1031 | initDurationsArray.length * 9
1032 | );
1033 |
1034 | // ********** Task 3: Inital duration (number - milliseconds) **********
1035 | console.log(
1036 | 'Here is an array of the times our lambda function has been through a cold start in the past day: ',
1037 | initDurationsArray
1038 | );
1039 |
1040 | // ********** Task 4: Cold calls/ week (number) **********
1041 | const averageColdCalls = initDurationsArray.length;
1042 | console.log(
1043 | 'This is the average number of cold calls per week: ',
1044 | averageColdCalls
1045 | );
1046 |
1047 | // ********** Task 5: Average Initialization Duration (number - milliseconds ) **********
1048 | const findAverageInit = function (array) {
1049 | let sum = 0;
1050 | for (let i = 0; i < array.length; i++) {
1051 | sum += Number(array[i].initDuration);
1052 | }
1053 | return sum / initDurationsArray.length;
1054 | };
1055 | const averageInitDuration = findAverageInit(initDurationsArray);
1056 | console.log(
1057 | 'This is the average init time (milliseconds): ',
1058 | averageInitDuration
1059 | );
1060 | // *********************************************************************
1061 | // Reminder: this is a limit of 200 in the queryString - take away the limit and it will get all the queries in the past week because of the params.endTime and params.startTime
1062 |
1063 |
1064 | // ********** Task 5: Average DurRun **********
1065 |
1066 | // ********** Task 6: Average DurCold **********
1067 |
1068 | // ********** Task 7: Average DurRun **********
1069 |
1070 | module.exports = { averageInitDuration , averageColdCalls , initDurationsArray };
--------------------------------------------------------------------------------