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 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
2. Setup Amplify
Initiate Amplify project
amplify init
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
Then install aws-amplify
package
npm i aws-amplify
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 │
└──────────┴───────────────┴───────────┴─────────────────┘
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 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
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"
}
}
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
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"
}
}
}
Note: I left some content in file to reduce confusion.
Then deploy
amplify publish
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 │
└──────────────┴───────────────────────────────────────────┘
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 editingnext.config.js
by adding
const nextConfig = {
images: {
unoptimized: true,
},
}
- 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())
}, [])
}
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 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 │
└──────────────┴───────────────────────────────────────────┘
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
andunauthRole
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"
}
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,
}
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())
}, [])
}
Test it
npm run dev
Then check log on CloudWatch and wait for the magic
6. Deploy
amplify publish
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)