Apache Access Analyzer
Build a Perl tool that reads Apache web server access logs and turns them into useful reports: requests per hour, the share of each status code (how many 200s, 404s and 500s), the busiest pages, the slowest ones, and the addresses causing the most errors. The totals are saved to an SQLite database with DBI, so logs can be imported every day and reports can cover weeks, and a log file already imported is never counted twice. The tool can also post a daily summary to a chat webhook with LWP::UserAgent. You'll learn parsing log lines with regular expressions, aggregating data with hashes, using DBI with transactions, making tools safe to re-run, and testing with Test::More.
- Estimate
- ~11h
- Steps
- 6
- Completed by
- 0
- Proposed by
- codeseed.app
CPAN · DBI · LWP::UserAgent
Project roadmap
- 01
Define inputs, outputs and storage
~1.5hWrite README.md: the input is Apache logs in combined format (plain or .gz), the reports (hourly traffic, status codes, top pages, slowest pages if the log has a response time field, top error sources), and the SQLite tables (hourly totals per path and status, plus the imported files with their size and checksum). Set up a cpanfile with DBI, DBD::SQLite and LWP::UserAgent. Done when: README.md shows example input and output.
- 02
Parse log lines
~2hWrite a parser module with a regular expression for the combined format that extracts the IP, time, method, path, status, size, referrer and user agent. Convert the time to UTC and truncate it to the hour. Remove query strings from paths so /page?id=1 and /page?id=2 count as one. Count lines that don't match instead of failing, and show a few examples. Done when: a sample log parses with the right fields, and malformed lines are counted.
- 03
Aggregate and store with DBI
~2.5hAggregate in memory by hour, path and status (count, bytes, total response time), then write the totals to SQLite with prepared statements inside one transaction per file. Record the file's checksum in the imported files table in the same transaction, and skip any file already imported, so running the import twice counts nothing twice. Done when: importing the same log twice leaves the totals unchanged.
- 04
Produce reports
~2hAdd a report command with --from and --to dates that runs SQL queries on the stored totals and prints: requests per hour as a simple text bar chart, status codes with percentages, the 10 busiest paths, the slowest paths by average time, and the top IPs producing errors. Add --format json. Done when: the reports match counts made by hand on a small log.
- 05
Post a summary with LWP
~1hAdd --webhook URL, which posts a short daily summary (total requests, error rate, busiest page) as JSON using LWP::UserAgent with a timeout, retrying once on a network error. A failed post prints a warning but never loses the imported data. Done when: the summary reaches a test webhook, and an unreachable URL only causes a warning.
- 06
Test it
~2hWrite tests in t/ with Test::More: parsing valid and malformed lines, removing query strings, hour grouping across midnight, importing twice (the second import is skipped), and report numbers on a small fixture log with a temporary SQLite database. Done when: prove -l t/ passes.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack