Files
labs/Lab-2/Lab-2-Practical/03_Data_Structures_Log_Parsing_Lab.ipynb

5.3 KiB

Module 2 — Lab 3: Data Structures and Raw Log Analysis

Topics: list, tuple, dictionary, set, parsing

Instructions: Run the cells in order. Complete every TODO, then run the Self-check cell. Do not open the Solution notebook before attempting the tasks yourself.

Input data

Each log line contains four comma-separated fields: timestamp,source_ip,username,status.

In [ ]:
raw_logs = [
    "2026-09-01T08:00:00Z,203.0.113.55,alice,SUCCESS",
    "2026-09-01T08:01:00Z,198.51.100.14,admin,FAILED",
    "2026-09-01T08:01:20Z,198.51.100.14,root,FAILED",
    "2026-09-01T08:02:00Z,192.0.2.88,bob,SUCCESS",
    "2026-09-01T08:02:30Z,198.51.100.14,guest,FAILED",
    "2026-09-01T08:03:00Z,192.0.2.99,carol,FAILED",
]

critical_fields = ("timestamp", "source_ip", "username", "status")
blocked_ips = {"198.51.100.14", "203.0.113.200"}

print(f"Rows: {len(raw_logs)}")
print("Critical fields:", critical_fields)

1. Convert one log line into a dictionary

In [ ]:
def parse_log_line(line):
    # TODO 1: split the line into four variables
    # TODO 2: return a dictionary with the four keys in critical_fields
    # Normalize username to lowercase and status to uppercase.
    pass


sample_event = parse_log_line(raw_logs[0])
print(sample_event)

2. Create an event list and a set of unique IP addresses

In [ ]:
# TODO 3: create the events list by parsing each raw log line
events = []

# TODO 4: create the unique_ips set from events
unique_ips = set()

print("Unique IPs:", sorted(unique_ips))
print("Number of unique IPs:", len(unique_ips))

3. Count failed logins with a dictionary

In [ ]:
failed_by_ip = {}

# TODO 5: count FAILED events for each source_ip.
# Hint: dictionary.get(ip, 0)

print(failed_by_ip)

4. Combine dictionaries and sets to create alerts

In [ ]:
alerts = []

# TODO 6: create an alert when count >= 3 OR the IP is in blocked_ips.
# Each alert is a dictionary containing source_ip, failed_attempts, and blocked.

print(alerts)

Self-check

In [ ]:
assert sample_event["username"] == "alice"
assert len(events) == 6
assert unique_ips == {"203.0.113.55", "198.51.100.14", "192.0.2.88", "192.0.2.99"}
assert failed_by_ip["198.51.100.14"] == 3
assert alerts == [{"source_ip": "198.51.100.14", "failed_attempts": 3, "blocked": True}]
print("✓ Lab 3 completed")

Reflection questions

  • Why is a set suitable for storing unique IP addresses or a blocklist?
  • Why is a tuple suitable for field names that should not change?