A Bash script that scans log files for errors, saves a structured report, and alerts you when things go wrong. Started as a local automation tool, then extended into a full AWS monitoring pipeline with real-time email alerts.
If you've worked in a DevOps or cloud engineering role, you know the drill: multiple services dumping logs into a directory, and someone has to manually grep through them every morning looking for errors. It's repetitive, easy to miss things, and a genuine waste of time.
That was the starting point. What used to take 30–45 minutes of manual command execution per day — checking for ERROR, FATAL, and CRITICAL entries across multiple log files — now runs in seconds with a single command, and fires an email automatically when something actually needs attention.
The full flow once deployed on AWS:
Shell Script (Bash)
↓
EC2 Instance ← IAM Role (CloudWatchAgentServerPolicy)
↓
CloudWatch Agent
↓
CloudWatch Logs (log group: log-analyzer)
↓
Metric Filter (ERROR → ErrorCW metric)
↓
CloudWatch Alarm (threshold: ErrorCW > 5)
↓
Amazon SNS (topic: log-alerts)
↓
Email Notification
- Scans only log files modified in the last 24 hours — no point re-checking files that haven't changed
- Searches for configurable error patterns:
ERROR,FATAL,CRITICAL - Counts occurrences per pattern, per file
- Saves a full structured report to a
.txtfile - Prints an
ALERTto stdout if any pattern count exceeds your defined threshold - Easy to adapt — everything configurable at the top of the file
#!/bin/bash
# analyze-logs.sh
# Scans log files for errors and saves a report
# Sergiu Gota — github.com/sergiugotacloud
LOG_DIR="/home/ec2-user/logs"
REPORT_FILE="/home/ec2-user/logs/log_analysis_report.txt"
ERROR_PATTERNS=("ERROR" "FATAL" "CRITICAL")
THRESHOLD=10
# make sure the logs folder actually exists before we do anything
if [ ! -d "$LOG_DIR" ]; then
echo "ERROR: Log directory not found: $LOG_DIR"
exit 1
fi
# start fresh report each run
echo "Log Analysis Report" > "$REPORT_FILE"
echo "======================" >> "$REPORT_FILE"
echo "Run at: $(date)" >> "$REPORT_FILE"
echo -e "\nLog files modified in last 24h:" >> "$REPORT_FILE"
LOG_FILES=$(find "$LOG_DIR" -name "*.log" -mtime -1)
# nothing to do if no files were updated recently
if [ -z "$LOG_FILES" ]; then
echo "No log files modified in the last 24h." >> "$REPORT_FILE"
exit 0
fi
echo "$LOG_FILES" >> "$REPORT_FILE"
# loop through each log file and check for each error pattern
find "$LOG_DIR" -name "*.log" -mtime -1 -print0 | while IFS= read -r -d '' LOG_FILE; do
echo -e "\n===================================" >> "$REPORT_FILE"
echo "Processing: $LOG_FILE" >> "$REPORT_FILE"
echo "===================================" >> "$REPORT_FILE"
for PATTERN in "${ERROR_PATTERNS[@]}"; do
echo -e "\nChecking: $PATTERN" >> "$REPORT_FILE"
MATCHES=$(grep "$PATTERN" "$LOG_FILE" 2>/dev/null || true)
COUNT=$(grep -c "$PATTERN" "$LOG_FILE" 2>/dev/null)
echo "$MATCHES" >> "$REPORT_FILE"
echo "Count: $COUNT" >> "$REPORT_FILE"
# if count goes above threshold, flag it in the report
# adjust THRESHOLD at the top depending on how noisy your logs are
if [ "$COUNT" -gt "$THRESHOLD" ]; then
echo "NOTICE: High number of $PATTERN in $LOG_FILE" >> "$REPORT_FILE"
fi
done
done
echo -e "\nLog analysis completed."
echo "Report saved at: $REPORT_FILE"curl -O https://raw.githubusercontent.com/YOUR_GITHUB/log-analyzer/main/analyze-logs.shLOG_DIR="/path/to/your/logs" # where your .log files live
REPORT_FILE="/path/to/report.txt" # where to save the output
ERROR_PATTERNS=("ERROR" "FATAL" "CRITICAL")
THRESHOLD=10 # alert if count exceeds thischmod +x analyze-logs.sh
./analyze-logs.shRun daily at 8am without thinking about it:
crontab -e
# Add:
0 8 * * * /home/ec2-user/analyze-logs.shAmazon Linux 2023, t3.micro, eu-central-1. Attach an IAM role with CloudWatchAgentServerPolicy — the CloudWatch Agent needs this to push logs.
ssh -i YOURKEYPAIR.pem ec2-user@YOUR_EC2_IPOnce connected, create the logs directory and drop your sample log files in:
mkdir -p ~/logs
# Upload your log files via scp or create test ones:
echo "ERROR: payment failed" >> ~/logs/application.log
echo "FATAL: database down" >> ~/logs/application.log
echo "CRITICAL: disk full" >> ~/logs/system.log# From your local machine:
scp -i YOURKEYPAIR.pem analyze-logs.sh ec2-user@YOUR_EC2_IP:~/
# On the instance:
chmod +x ~/analyze-logs.sh
./analyze-logs.shThe script creates a report at ~/logs/log_analysis_report.txt. You can read it with:
cat ~/logs/log_analysis_report.txtInject 12 errors to trigger the alert logic (threshold is set to 10):
for i in {1..12}; do echo "ERROR: overload" >> ~/logs/system.log; done
./analyze-logs.shYou'll see this in stdout:
ALERT: ERROR issue detected in /home/ec2-user/logs/system.log
sudo dnf install amazon-cloudwatch-agent -yCreate the config file at /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/home/ec2-user/logs/application.log",
"log_group_name": "log-analyzer",
"log_stream_name": "application"
},
{
"file_path": "/home/ec2-user/logs/system.log",
"log_group_name": "log-analyzer",
"log_stream_name": "system"
}
]
}
}
}
}Start the agent:
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config \
-m ec2 \
-s \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.jsonVerify it's running:
sudo systemctl status amazon-cloudwatch-agentGo to CloudWatch → Log groups → log-analyzer. You should see two log streams: application and system.
In the AWS Console, go to Log groups → log-analyzer → Metric filters → Create metric filter:
- Filter pattern:
ERROR - Metric namespace:
LogAnalyzer - Metric name:
ErrorCW - Metric value:
1
This turns every ERROR line in the logs into a countable CloudWatch metric.
Go to CloudWatch → Alarms → Create alarm:
- Select metric:
LogAnalyzer → ErrorCW - Threshold: greater than
5for1 out of 1datapoints, 1-minute period - Action: send notification to an SNS topic
Create an SNS topic called log-alerts if you don't have one, and subscribe your email to it. You'll get a confirmation email — make sure to confirm the subscription or alerts won't deliver.
When the ErrorCW metric crosses the threshold, you get an email within about a minute:
The email includes the alarm name, timestamp, metric details, and a direct link to the alarm in the AWS Console.
| # | File | What it shows |
|---|---|---|
| 1 | 1. ec2-instance.png |
EC2 instance log-analyzer running in eu-central-1 |
| 2 | 2. ssh-connection.png |
SSH into Amazon Linux 2023 |
| 3 | 3. script-execution.png |
Running ./analyze-logs.sh on the instance |
| 4 | 4. report-output.png |
Report output broken down by file and pattern |
| 5 | 5. alert-trigger.png |
ALERT stdout message when threshold is exceeded |
| 6 | 6-cloudwatch-log-group.png |
log-analyzer log group in CloudWatch |
| 7 | 7-log-streams.png |
system and application log streams with timestamps |
| 8 | 8-alarm-in-alarm.png |
CloudWatch alarm in ALARM state, ErrorCW > 5 |
| 9 | 9-email-alert.png |
SNS email notification from Frankfurt region |
| 10 | 10-sns-subscription.png |
SNS subscription confirmation |
| 11 | 11. architecture-diagram.png |
Full architecture diagram |
The script is kept intentionally simple so it's easy to adapt. Common changes:
Add more error patterns
ERROR_PATTERNS=("ERROR" "FATAL" "CRITICAL" "EXCEPTION" "TIMEOUT")Change the time window — look back 2 days instead of 1:
LOG_FILES=$(find "$LOG_DIR" -name "*.log" -mtime -2)Lower the threshold for sensitive services:
THRESHOLD=3Send alert to Slack instead of just stdout:
# Replace the echo "ALERT..." line with:
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"ALERT: $PATTERN spike in $LOG_FILE\"}" \
YOUR_SLACK_WEBHOOK_URLSend local email alert (if mail is configured):
echo "ALERT: $PATTERN in $LOG_FILE" | mail -s "Log Alert" your@email.comScript runs but no log files are processed
Check if the find command is returning anything:
find /home/ec2-user/logs -name "*.log" -mtime -1If empty, your log files are either older than 24h or in a different path. Either update LOG_DIR or touch the files to update their modification timestamp:
touch ~/logs/application.logCloudWatch Agent is installed but no logs appearing in console
Check the agent status:
sudo systemctl status amazon-cloudwatch-agentCheck agent logs for errors:
sudo tail -f /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.logCommon cause: the IAM role isn't attached to the instance, or it doesn't have CloudWatchAgentServerPolicy. Verify under EC2 → Instance → Security → IAM role.
Alarm stays in INSUFFICIENT_DATA state
This means no data points have been received yet. The metric filter only creates a data point when a matching log line arrives. Inject a few errors and run the script again:
for i in {1..6}; do echo "ERROR: test" >> ~/logs/system.log; done
./analyze-logs.shWait 60–90 seconds for the alarm to evaluate.
Email subscription confirmed but no alert email received
Double check the SNS subscription status in the AWS Console — it needs to show Confirmed, not PendingConfirmation. Also check spam. AWS SNS emails come from no-reply@sns.amazonaws.com and sometimes get filtered.
Permission denied when running the script
chmod +x ~/analyze-logs.shReport file shows no matches even though errors exist
grep is case-sensitive by default. If your logs use lowercase (error instead of ERROR), either update the patterns or add the -i flag:
COUNT=$(grep -ci "$PATTERN" "$LOG_FILE")- Bash 4+ (default on Amazon Linux; macOS ships with 3.x — install newer via
brew install bash) - Standard Linux tools:
find,grep— nothing to install - For the AWS extension: EC2 with IAM role, CloudWatch Agent, and SNS topic with confirmed email subscription
This started as a manual process — grep through logs, count errors, repeat. Turning it into a script removed the repetition. Deploying it on EC2 with CloudWatch and SNS removed the need to even remember to run it. Now errors surface as email alerts automatically, and there's a full audit trail in CloudWatch Logs for when you need to dig deeper.
It's a small project but it reflects a real shift in how monitoring works: from reactive (someone notices something is wrong) to proactive (the system tells you before it becomes a problem).
Sergiu Gota AWS Certified Solutions Architect – Associate · AWS Cloud Practitioner









