Full Stack • Java • System Design • Cloud • AI Engineering

AWS CloudWatch Logs with Spring Boot

Step-by-step guide to send Spring Boot application logs to AWS CloudWatch Logs using EC2, IAM Role, CloudWatch Agent, and Logback.


Introduction

In production, logs should not stay only inside the server.

If your Spring Boot application runs on AWS EC2, ECS, EKS, or Lambda, you need a centralized logging system to collect, search, monitor, and troubleshoot logs.

AWS CloudWatch Logs helps you:

  • Store application logs centrally
  • Search logs using Log Insights
  • Create alerts from log patterns
  • Debug production issues faster
  • Monitor errors, exceptions, and API failures

Real-Time Use Case

Assume you have a Spring Boot order service running on EC2.

When a user places an order:

POST /orders

The application logs:

Order received
Payment validation started
Inventory updated
Order completed

Instead of checking logs manually inside EC2, we send logs to AWS CloudWatch Logs.


Architecture

flowchart LR
    U[User] --> ALB[Application Load Balancer]
    ALB --> EC2[EC2 Instance Running Spring Boot App]

    EC2 --> APPLOG[Application Log File]
    APPLOG --> AGENT[CloudWatch Agent]
    AGENT --> CWL[CloudWatch Logs]

    CWL --> LI[CloudWatch Log Insights]
    CWL --> ALARM[CloudWatch Alarms]
    ALARM --> SNS[SNS Notification]

Step 1: Create Spring Boot Application

Create a Spring Boot REST controller.

package com.codewithvenu.orders.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
@Slf4j
public class OrderController {

    @PostMapping
    public String createOrder() {
        log.info("Order request received");

        try {
            log.info("Payment validation started");
            log.info("Inventory validation completed");
            log.info("Order created successfully");

            return "Order created successfully";
        } catch (Exception ex) {
            log.error("Order creation failed", ex);
            return "Order creation failed";
        }
    }

    @GetMapping("/{id}")
    public String getOrder(@PathVariable String id) {
        log.info("Fetching order details for orderId={}", id);
        return "Order details for " + id;
    }
}

Step 2: Configure Logback

Create this file:

src/main/resources/logback-spring.xml
<configuration>

    <property name="LOG_PATH" value="/opt/apps/order-service/logs"/>
    <property name="APP_NAME" value="order-service"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}.log</file>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>

        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

Step 3: Build Spring Boot JAR

mvn clean package -DskipTests

Generated JAR:

target/order-service.jar

Step 4: Copy JAR to EC2

scp -i my-key.pem target/order-service.jar ec2-user@<EC2_PUBLIC_IP>:/home/ec2-user/

Login to EC2:

ssh -i my-key.pem ec2-user@<EC2_PUBLIC_IP>

Create application folder:

sudo mkdir -p /opt/apps/order-service/logs
sudo cp /home/ec2-user/order-service.jar /opt/apps/order-service/
sudo chown -R ec2-user:ec2-user /opt/apps/order-service

Run application:

java -jar /opt/apps/order-service/order-service.jar

Test API:

curl -X POST http://localhost:8080/orders

Check local log file:

cat /opt/apps/order-service/logs/order-service.log

Step 5: Create IAM Role for EC2

Create an IAM Role and attach this AWS managed policy:

CloudWatchAgentServerPolicy

Attach this role to your EC2 instance.

Required permission flow:

flowchart TD
    EC2[EC2 Instance] --> ROLE[IAM Role]
    ROLE --> POLICY[CloudWatchAgentServerPolicy]
    POLICY --> CWL[CloudWatch Logs Permission]

Step 6: Install CloudWatch Agent on EC2

For Amazon Linux:

sudo yum update -y
sudo yum install amazon-cloudwatch-agent -y

Verify installation:

amazon-cloudwatch-agent-ctl -h

Step 7: Create CloudWatch Agent Config

Create config file:

sudo vi /opt/aws/amazon-cloudwatch-agent/bin/config.json

Add this configuration:

{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/opt/apps/order-service/logs/order-service.log",
            "log_group_name": "/codewithvenu/springboot/order-service",
            "log_stream_name": "{instance_id}",
            "timezone": "UTC"
          }
        ]
      }
    }
  }
}

Step 8: Start CloudWatch Agent

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config \
-m ec2 \
-c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json \
-s

Check agent status:

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status

Step 9: Verify Logs in AWS Console

Go to:

AWS Console → CloudWatch → Logs → Log groups

Find:

/codewithvenu/springboot/order-service

Open the log stream and check application logs.


Step 10: Generate Application Logs

Call the APIs:

curl -X POST http://localhost:8080/orders
curl http://localhost:8080/orders/101

Expected logs:

Order request received
Payment validation started
Inventory validation completed
Order created successfully
Fetching order details for orderId=101

Step 11: Query Logs Using CloudWatch Log Insights

Go to:

CloudWatch → Logs Insights

Select log group:

/codewithvenu/springboot/order-service

Query latest logs:

fields @timestamp, @message
| sort @timestamp desc
| limit 20

Find error logs:

fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 50

Find order-related logs:

fields @timestamp, @message
| filter @message like /Order/
| sort @timestamp desc
| limit 50

Step 12: Create Metric Filter for Errors

Create a metric filter to track application errors.

Pattern:

ERROR

Metric details:

Metric Namespace: CodeWithVenu/Application
Metric Name: OrderServiceErrorCount
Metric Value: 1

Flow:

flowchart LR
    LOGS[CloudWatch Logs] --> FILTER[Metric Filter: ERROR]
    FILTER --> METRIC[Custom Metric]
    METRIC --> ALARM[CloudWatch Alarm]
    ALARM --> SNS[SNS Email Alert]

Step 13: Create CloudWatch Alarm

Create alarm for error count.

Example condition:

If OrderServiceErrorCount >= 1 for 1 datapoint within 5 minutes

Send notification to SNS email topic.


For production systems, use structured logs.

Example JSON log:

{
  "timestamp": "2026-06-30T10:15:30Z",
  "level": "INFO",
  "service": "order-service",
  "traceId": "abc-123",
  "orderId": "101",
  "message": "Order created successfully"
}

This helps with:

  • Faster searching
  • Better debugging
  • Traceability
  • Alerting
  • Audit logs

Step 15: Better Logback Pattern with Trace ID

If you use Spring Cloud Sleuth, Micrometer Tracing, or OpenTelemetry, include trace ID.

<pattern>
%d{yyyy-MM-dd HH:mm:ss} %-5level traceId=%X{traceId} spanId=%X{spanId} [%thread] %logger{36} - %msg%n
</pattern>

Example output:

2026-06-30 10:15:30 INFO traceId=abc123 spanId=xyz789 Order created successfully

Common Issues and Fixes

Issue Reason Fix
Logs not visible in CloudWatch Agent not running Check CloudWatch Agent status
Access denied IAM role missing permission Attach CloudWatchAgentServerPolicy
Log group not created Wrong config or permission issue Validate config file
Empty log stream Wrong file path Check application log path
Old logs missing Log rotation issue Configure proper log rotation
High log cost Too many debug logs Use INFO in production

Best Practices

Use these practices in enterprise applications:

1. Use INFO for business flow logs
2. Use ERROR only for real failures
3. Avoid logging passwords, tokens, PII, card numbers, or secrets
4. Add requestId, traceId, userId, orderId where required
5. Use structured JSON logs for large systems
6. Set log retention period
7. Create alarms for ERROR and critical failures
8. Avoid DEBUG logs in production
9. Use centralized dashboards
10. Connect logs with metrics and traces

Spring Boot Logging Flow

sequenceDiagram
    participant User
    participant API as Spring Boot API
    participant Logback
    participant File as Local Log File
    participant Agent as CloudWatch Agent
    participant CW as CloudWatch Logs

    User->>API: Call REST API
    API->>Logback: log.info / log.error
    Logback->>File: Write logs to file
    Agent->>File: Read log file
    Agent->>CW: Push logs to log group

Final Folder Structure

order-service
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── codewithvenu
│       │           └── orders
│       │               └── controller
│       │                   └── OrderController.java
│       └── resources
│           ├── application.yml
│           └── logback-spring.xml
├── pom.xml
└── target
    └── order-service.jar

Final Summary

AWS CloudWatch Logs is one of the most important observability services for Spring Boot applications running on AWS.

In this implementation:

Spring Boot writes logs
CloudWatch Agent reads logs
CloudWatch Logs stores logs
Log Insights searches logs
Metric Filter tracks errors
CloudWatch Alarm sends alerts

This setup gives production teams a centralized logging system for debugging, monitoring, and alerting.


Loading likes...

Comments

Share a question, correction, or practical insight about this article.

Loading approved comments...