DEV Community

Lumin
Lumin

Posted on

How to make client-side logger with Amplify and CloudWatch

The client-side log is different from the server-side log because it didn't write into the file system and it's not easy to control it. I'm using AWS and I don't want to use SaaS service to feed the log into CloudWatch (why do I have to send the log to other services and send it back into CloudWatch!)

Goal

  • Run Next.js SSG (Static Site Generator) on Amplify Hosting (or S3 CloudFront)
  • Enable client-side log without sending API to lambda

TLDR

  • Amplify has a Logger class and CloudWatch provider, so we can feed log from the browser to CloudWatch
  • Need extra effort to use AWS Cognito to allow an unauthorized users to send log events to CloudWatch

Note

This tutorial runs on

  • Node.js version v18.15.0
  • Next.js version 13.2.4
  • Amplify version 11.0.2

Prerequisite

Steps

1. Setup Next.js project

Just create a new Next.js project

npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode

Enter Next.js wizard

  • What is your project named? -> "PROJECT-NAME"
  • Would you like to use TypeScript with this project? -> "Yes"
  • Would you like to use ESLint with this project? -> "Yes"
  • Would you like to use src/ directory with this project? -> "Yes"
  • Would you like to use experimental app/ directory with this project? -> "Yes"
  • What import alias would you like configured? -> "@/*"

Then verify (it suppose to run Next.js app)

cd PROJECT-NAME/
npm run dev
Enter fullscreen mode Exit fullscreen mode

2. Setup Amplify

Initiate Amplify project

amplify init 
Enter fullscreen mode Exit fullscreen mode

Enter wizard input

  • Enter a name for the project -> "PROJECT-NAME"
  • Initialize the project with the above configuration? -> "Yes"
  • Select the authentication method you want to use: -> "AWS profile"
  • Please choose the profile you want to use -> "default"

When we submit the input, Amplify will generate a project directory on amplify/ and a resources file on src/aws-exports.js, the last one will re-generate when the project resource has changed.

Inside amplify/, there are some files we can modify, one of which is /amplify/.config/project-config.json which is the configuration about the environment and build script that we can update later.

Project information
| Name: PROJECT-NAME
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react
| Source Directory Path: src
| Distribution Directory Path: build
| Build Command: npm run-script build
| Start Command: npm run-script start
Enter fullscreen mode Exit fullscreen mode

Then install aws-amplify package

npm i aws-amplify
Enter fullscreen mode Exit fullscreen mode

We can verify by cli (suppose to show empty resource, since we didn't do anything yet)

amplify status 

    Current Environment: dev

┌──────────┬───────────────┬───────────┬─────────────────┐
│ Category │ Resource name │ Operation │ Provider plugin │
└──────────┴───────────────┴───────────┴─────────────────┘
Enter fullscreen mode Exit fullscreen mode

We can go check on AWS CloudTrail (suppose to show that we have created resources on Amplify, CloudFormation, and S3)

3. Deploy SSG

We are going to deploy Next.js in SSG (Static Site Generator) on Amplify Hosting (alternatively, we can deploy with S3 + CloudFront)

Add hosting resource

# this is how we add resource with Amplify cli
amplify add hosting
Enter fullscreen mode Exit fullscreen mode

Enter wizard input

  • Select the plugin module to execute -> "Hosting with Amplify Console" (Managed hosting with custom domains, Continuous deployment)
  • Choose a type -> "Manual deployment"

Verify (suppose to show that we have created a new resource, which is Amplify Hosting)

amplify status

    Current Environment: dev

┌──────────┬────────────────┬───────────┬───────────────────┐
│ Category │ Resource name  │ Operation │ Provider plugin   │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Hosting  │ amplifyhosting │ Create    │ awscloudformation │
└──────────┴────────────────┴───────────┴───────────────────┘


No amplify console domain detected
Enter fullscreen mode Exit fullscreen mode

Run deploy

Before deploying, we have to update the build script to support SSG, tell Next.js to build for the static host, by editing package.json

{
  "script": {
    "build": "next build && next export"
  }
}
Enter fullscreen mode Exit fullscreen mode

Verify by build and run on the static host tool (we might need to install serve cli by npm i -g serve)

npm run build 

serve out # we should have the same local as the previous step
Enter fullscreen mode Exit fullscreen mode

We have to update Amplify config to use dist folder as out since the default exported SSG folder will be out by editing amplify/.config/project-config.json

{
  "javascript": {
    "config": {
      "DistributeDir": "out"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: I left some content in file to reduce confusion.

Then deploy

amplify publish
Enter fullscreen mode Exit fullscreen mode

Verify again with amplify status (suppose to update Amplify Hosting status and show hosting url)

amplify status

    Current Environment: dev

┌──────────┬────────────────┬───────────┬───────────────────┐
│ Category │ Resource name  │ Operation │ Provider plugin   │
├──────────┼────────────────┼───────────┼───────────────────┤
│ Hosting  │ amplifyhosting │ No Change │ awscloudformation │
└──────────┴────────────────┴───────────┴───────────────────┘


Amplify hosting urls:
┌──────────────┬───────────────────────────────────────────┐
│ FrontEnd Env │ Domain                                    │
├──────────────┼───────────────────────────────────────────┤
│ dev          │ https://dev.--------------.amplifyapp.com │
└──────────────┴───────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Note:

  • When changing a build script to support SSG, we might encounter the error “Error: Image Optimization using Next.js' default loader is not compatible with next export.” fix it by editing next.config.js by adding
  const nextConfig = {
    images: {
      unoptimized: true,
    },
  }
Enter fullscreen mode Exit fullscreen mode
  • Next.js are going to deprecate next export, so this solution might not work in the future
  • I prefer Amplify Hosting to S3+CloudFront because I don’t want to maintain and it has advanced features like preview or deploy on new branch.

4. Enable client-side log

Adding some log for test

Added console.log on src/pages/index.tsx when page render (or rerender)

export default function Home() {
  useEffort(() => {
    console.log('log created on', Date.now())
  }, [])
}
Enter fullscreen mode Exit fullscreen mode

Explain: log will show when this page render or rerender (in development it will show 2 lines of log)

Add auth resource

amplify add auth
Enter fullscreen mode Exit fullscreen mode

Enter wizard input

  • Do you want to use the default authentication and security configuration? -> "Default configuration"
  • How do you want users to be able to sign in? -> "Email"
  • Do you want to configure advanced settings? -> "No"

Verify

amplify status

    Current Environment: dev

┌──────────┬─────────────────────┬───────────┬───────────────────┐
│ Category │ Resource name       │ Operation │ Provider plugin   │
├──────────┼─────────────────────┼───────────┼───────────────────┤
│ Auth     │ ___________________ │ Create    │ awscloudformation │
├──────────┼─────────────────────┼───────────┼───────────────────┤
│ Hosting  │ amplifyhosting      │ No Change │ awscloudformation │
└──────────┴─────────────────────┴───────────┴───────────────────┘


Amplify hosting urls:
┌──────────────┬───────────────────────────────────────────┐
│ FrontEnd Env │ Domain                                    │
├──────────────┼───────────────────────────────────────────┤
│ dev          │ https://dev.______________.amplifyapp.com │
└──────────────┴───────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

We’ll see newly created resource “Auth”

Next, we’ll have to enter the AWS Console to modify and add some resources to enable the client-side log

Update identities setting

Enable unauthenticated identities on AWS Cognito / Federated Identities

  • goto AWS Console / Cognito / Federated Identities (on the left menu)
  • choose identities pool
  • click edit identity pool (on the right side)
  • checked on “Enable access to unauthenticated identities” under “Unauthenticated identities”
  • don't forget to "Save"

Update identities role permission

Update role permission to allow user to writing log

  • go to AWS Console / IAM / Roles (on the left menu)
  • search role with project name. It should show 3 roles
    • amplify-PROJECT-NAME-ENV-ID-authRole — for authenticated identities
    • amplify-PROJECT-NAME-ENV-ID-authRole-idp — for lambda of authentication service
    • amplify-PROJECT-NAME-ENV-ID-unauthRole — for unauthenticated identities
  • attach these policies into authRole and unauthRole to allowed write log on every resources of current account
  {
    "Action": [
      "logs:DescribeLogGroup",
      "logs:DescribeLogStream",
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": "arn:aws:logs:*:ACCOUNT-ID:*",
    "Effect": "Allow"
  }
Enter fullscreen mode Exit fullscreen mode

Create log group and log stream

Create log group and log stream in CloudWatch, we will use log group name and log stream name as config in our code.

Next, it's a time to write code 😝

5. Update code to use Amplify Logger

Create utilities file

create src/aws.ts

import { Amplify, AWSCloudWatchProvider, Logger } from 'aws-amplify'
import awsExports from './aws-exports'

// initiate Amplify
Amplify.configure(awsExports)

// initiate logger
const logger = new Logger('PROJECT-NAME', 'DEBUG')
Amplify.register(logger)
logger.addPluggable(new AWSCloudWatchProvider({
  logGroupName: 'LOG-GROUP-NAME',
  logStreamName: 'LOG-STREAM-NAME',
  region: awsExports.aws_project_region,
}))

export {
  logger,
}
Enter fullscreen mode Exit fullscreen mode

Use logger

Switch from console.log to logger.info on src/pages/index.tsx

import { logger } from '@/aws'

export default function Home() {
  useEffect(() => {
    logger.info('log created on', Date.now())
  }, [])
}
Enter fullscreen mode Exit fullscreen mode

Test it

npm run dev
Enter fullscreen mode Exit fullscreen mode

Then check log on CloudWatch and wait for the magic

6. Deploy

amplify publish
Enter fullscreen mode Exit fullscreen mode

Summary

  • We have deployed static React into Amplify Hosting
  • We have used Amplify Logger and CloudWatch provider to feed log to CloudWatch
  • We have allowed the unauthorized users to write log

Improvement

  • Instead of manually modifying resources on AWS Console, use AWS CDK
  • Use logger library instead of Amplify Logger i.g., loglevel (supported browser and Node.js)
  • Add some images into blog post 😝

Top comments (0)