├── .DS_Store ├── .env ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── __tests__ ├── AWS_SDK.test.tsx └── page.test.tsx ├── jest.config.js ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── next.svg └── vercel.svg ├── src └── app │ ├── api │ └── send │ │ └── route.tsx │ ├── components │ ├── Accordions.tsx │ ├── Button.tsx │ ├── LineChart.tsx │ ├── Navbar.tsx │ ├── WarmButton.tsx │ ├── WarmPeriodTabs.tsx │ ├── badges.tsx │ ├── button0.jsx │ ├── calculations-updated.js │ ├── data.js │ ├── email-template.tsx │ └── functionrow.tsx │ ├── email │ ├── page.module.css │ └── page.tsx │ ├── favicon.ico │ ├── globals.css │ ├── home │ ├── dataPage │ │ ├── button1.jsx │ │ └── page.tsx │ └── functionList │ │ └── page.tsx │ ├── layout.tsx │ ├── page.tsx │ ├── retrievedData.js │ └── retrievedData2.js ├── tailwind.config.js ├── tailwind.config.ts └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Embr/cf406205887238ca1f12d8f2e6ad3d5481ac67f8/.DS_Store -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | RESEND_API_KEY="re_XBt7n2JA_PNy4EjkECAMcmEhC1Sx9mfuZ" 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /__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 | }); -------------------------------------------------------------------------------- /__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 | }); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | env: { 4 | }, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | 44 | 54 | 64 | 65 | 66 | 67 | 68 | 69 | 78 | 87 | 96 | 105 | 106 | 107 | 108 | 117 | 126 | 135 | 144 | 145 | 146 |
42 | Function name 43 | 52 | Cold calls /week 53 | 62 | Average cold start 63 |
76 | 1 77 | 85 | ChrisTestFunc 86 | 94 | 40 95 | 103 | 180ms 104 |
115 | 2 116 | 124 | ChrisPresentation 125 | 133 | 30 134 | 142 | 200ms 143 |
147 |
148 |
149 | ); 150 | }; 151 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 |
39 | 47 |
48 | 56 |
57 | 58 |
59 |
60 |
61 | ); 62 | }; 63 | 64 | export default Email; 65 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Embr/cf406205887238ca1f12d8f2e6ad3d5481ac67f8/src/app/favicon.ico -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |
58 |

{el}

59 |
60 | ))} */} 61 | Hello 62 |
63 | //
64 | //
65 | //

{newData.results}

66 | //
67 | //
68 | ); 69 | } 70 | 71 | export default GotData; 72 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 }; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------