Overview
Every web server writes an access log, one line per request. Reading thousands of lines by eye is impossible, but a few Linux tools joined with pipes can summarise them in seconds. In this project you will build analyze-log.sh, a reusable script that prints a clear report for any log in the standard “combined” format used by Nginx and Apache.
- A generator script that creates the same 200-line sample log for everyone.
- One-line pipelines that answer individual questions about the log.
- analyze-log.sh: a script combining them into one report, with input checking.
Prerequisites
- Pipes, cut, sort, uniq and awk — lesson 8.
- Shell scripts, arguments and if — lesson 12.
- A Linux terminal (lesson 2).
Step 1: Create Sample Data
Make a project folder with mkdir log-analyzer && cd log-analyzer. Instead of downloading a log, generate one: this script produces the same 200 requests on every computer, so your results will match this tutorial exactly.
#!/bin/bash# make-sample-log.sh: writes a realistic access.log so everyone gets the same datapages=(/ /about.html /projects.html /contact.html /blog.html /css/style.css)ips=(203.0.113.5 198.51.100.7 192.0.2.44 203.0.113.9 198.51.100.23)for i in $(seq 0 199); do ip=${ips[$(( (i * 7 + i / 3) % 5 ))]} page=${pages[$(( (i * 3 + i / 5) % 6 ))]} status=200 if (( i % 17 == 0 )); then page=/old-page.html; status=404; fi if (( i % 41 == 0 )); then page=/contact.html; status=500; fi hour=$(( 8 + (i * 5) % 11 )) printf '%s - - [24/Oct/2026:%02d:%02d:%02d +0000] "GET %s HTTP/1.1" %s %s\n' \ "$ip" "$hour" $(( i % 60 )) $(( (i * 13) % 60 )) "$page" "$status" $(( 500 + (i * 37) % 4000 ))done > access.logecho "Wrote $(wc -l < access.log) lines to access.log"chmod +x make-sample-log.sh./make-sample-log.shhead -n 3 access.logClick Run to see what this code prints.
Step 2: Explore with One-Liners
Before writing a script, answer each question on the command line. How many requests, and which distinct visitors (IP addresses)?
wc -l < access.logcut -d " " -f 1 access.log | sort -uClick Run to see what this code prints.
Which successful pages are most popular? Field 9 is the status code and field 7 the page:
awk '$9 == 200 {print $7}' access.log | sort | uniq -c | sort -rn | head -n 3Click Run to see what this code prints.
Which hours are busiest? Splitting the line on colons puts the hour in field 2:
cut -d ':' -f 2 access.log | sort | uniq -c | sort -rn | head -n 3Click Run to see what this code prints.
Step 3: Write the Script
Now combine the pipelines. The script takes the log file as an optional argument (defaulting to access.log), refuses missing files with a clear error, and prints each section under a heading:
#!/bin/bash# analyze-log.sh: summarise a web server access logset -euo pipefail
log="${1:-access.log}"if [ ! -f "$log" ]; then echo "Error: log file '$log' not found" >&2 exit 1fi
echo "=== Report for $log ==="echo "Total requests: $(wc -l < "$log")"echo "Unique visitors: $(cut -d ' ' -f 1 "$log" | sort -u | wc -l)"echoecho "--- Top 3 pages ---"awk '$9 == 200 {print $7}' "$log" | sort | uniq -c | sort -rn | head -n 3echoecho "--- Status codes ---"awk '{print $9}' "$log" | sort | uniq -c | sort -rnechoecho "--- Missing pages (404) ---"awk '$9 == 404 {print $7}' "$log" | sort | uniq -cechoecho "--- Busiest hour ---"cut -d ':' -f 2 "$log" | sort | uniq -c | sort -rn | head -n 1 | awk '{printf "%s:00 with %s requests\n", $2, $1}'set -euo pipefailstops the script if any command fails (lesson 12).log="${1:-access.log}"uses the first argument, or access.log if none is given.wc -l < "$log"reads the file through input redirection so wc prints only the number.- The last pipeline uses awk’s printf to turn “19 13” into a readable sentence.
Step 4: Run the Report
chmod +x analyze-log.sh./analyze-log.shClick Run to see what this code prints.
Eleven requests hit a page that no longer exists — a broken link worth fixing — and five caused server errors. Bad input is handled too:
./analyze-log.sh nope.logecho "exit code: $?"Click Run to see what this code prints.
Extend This Project
- Run it on a real log: on a server with Nginx, try sudo ./analyze-log.sh /var/log/nginx/access.log.
- Add a section listing IP addresses that caused more than five 404s — a sign of a bot scanning the site.
- Add an option to analyse only one day, using grep on the date before the rest of the pipeline.
- Save each report to a dated file and schedule it nightly with cron (see the backup project).
Summary
You turned a raw log into an actionable report with nothing but standard Linux tools: cut, sort, uniq and awk joined by pipes, wrapped in a robust script with argument handling and error checking.