# Apache Access Log Parser

This utility parses Apache access logs (Common or Combined format) to extract meaningful statistics. It calculates the distribution of HTTP status codes and identifies the top visiting IP addresses.

**Modules Used:**
*   [[programming/python/modules/re-module|re]]: To parse the log lines using regular expressions.
*   `collections`: To efficiently count occurrences of IPs and status codes.
*   [[programming/python/modules/argparse-module|argparse]]: To handle command-line arguments.

## The Code

Save this as `log_parser.py`.

```python
import re
import argparse
from collections import Counter
import os

# Regex pattern for Common Log Format (CLF) and Combined Log Format
# 1. IP Address
# 2. Identity (usually -)
# 3. User (usually -)
# 4. Timestamp
# 5. Request Method
# 6. Request Path
# 7. Protocol
# 8. Status Code
# 9. Size
LOG_PATTERN = re.compile(
    r'^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+) (\S+)\s*(\S+)?\s*" (\d{3}) (\S+)'
)

def parse_log_file(log_file):
    if not os.path.exists(log_file):
        print(f"Error: File '{log_file}' not found.")
        return

    print(f"Parsing '{log_file}'...")
    
    ip_counter = Counter()
    status_counter = Counter()
    total_requests = 0
    errors = 0

    try:
        with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
            for line in f:
                match = LOG_PATTERN.match(line)
                if match:
                    total_requests += 1
                    ip = match.group(1)
                    status = match.group(8)
                    
                    ip_counter[ip] += 1
                    status_counter[status] += 1
                else:
                    # Line didn't match pattern (maybe empty or malformed)
                    errors += 1
                    
    except Exception as e:
        print(f"Error reading file: {e}")
        return

    print(f"\nTotal Requests: {total_requests}")
    if errors > 0:
        print(f"Skipped Lines: {errors}")

    print("\n--- Status Code Distribution ---")
    for status, count in sorted(status_counter.items()):
        print(f"{status}: {count}")

    print("\n--- Top 10 Visitors (IPs) ---")
    print(f"{'IP Address':<20} {'Requests'}")
    print("-" * 30)
    for ip, count in ip_counter.most_common(10):
        print(f"{ip:<20} {count}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Apache Access Log Parser")
    parser.add_argument("logfile", help="Path to the Apache access log file")
    
    args = parser.parse_args()
    
    parse_log_file(args.logfile)
```

## Usage

```bash
python log_parser.py /var/log/apache2/access.log
```

[[programming/python/python]]