Audit Log That Reports One Line and Zero Errors
During an incident review, a junior admin shows this log tool:
log = open("audit.txt", "w") log.write("ERROR disk full") log.write("INFO retry") count = log.write("WARN slow") print(count) log.close()
with open("audit.txt") as f: lines = f.readlines() errors = 0 for line in lines: if line == "ERROR": errors += 1 print(len(lines), errors)
The tool prints 9 and then 1 0, but the admin expected three lines and one error. Worse, after the tool runs a second time the previous day's entries are gone. The audit file must survive across runs, must not duplicate old content, and must be easy to count by severity. The severity words always appear at the start of an entry, and entries are always written by this same script.
1. Explain why `len(lines)` is 1 and why `errors` stays 0, then give the two changes that fix both counts.
Read the worked answer
None of the three write calls includes "\n". write does not add line breaks, so the file ends up as one physical line: ERROR disk fullINFO retryWARN slow. readlines() splits on newline characters only, so it returns a one-element list - hence len(lines) == 1.
errors stays 0 because line == "ERROR" demands exact equality with the whole string. line is the entire 34-character record, so the comparison is false. A partial match requires a different operator or a normalised value:
if line.startswith("ERROR"): # or: if line.strip() == ...
errors += 1
And each write should terminate the record: log.write("ERROR disk full\n"). With both changes the file holds three lines and errors is 1. Note that startswith also matches ERRORS, so if that word is possible, compare the first token: line.split()[0] == "ERROR".
2. Explain the printed value 9 and why the previous day's entries disappear; give the corrected mode and closing pattern.
Read the worked answer
write returns the number of characters written, not a record count. "WARN slow" is 4 + 1 + 4 = 9 characters, so count is 9. Treating that number as a log-record tally is the actual mistake; the value is only useful for detecting short writes.
Mode "w" truncates the file to zero length the moment it is opened. Every run therefore destroys what was there, which is why yesterday's entries vanish. Use "a" to append, which creates the file if missing and otherwise positions writes at the end without erasing. If an existing file only needs reading, "r" is the correct mode and the default.
The bare open/close pair is also fragile: an exception before close() leaves the file unflushed and unlocked. Prefer the context manager, which closes even on error:
with open("audit.txt", "a") as log:
log.write("ERROR disk full\n")
3. Suppose the script is changed to mode `"a"` but the newline is still omitted, and the tool runs twice on an empty file. Describe the file contents, the line count, and what this shows about fixing one bug at a time.
Read the worked answer
Each run appends the same three chunks with no separators, so after two runs the file is one 68-character line: ERROR disk fullINFO retryWARN slowERROR disk fullINFO retryWARN slow.
readlines() still returns a one-element list, so len(lines) remains 1, and line == "ERROR" is still false. The mode change fixed durability but not parsing: old records survive, yet every run silently glues its entries onto the previous run's last record. This is a common trap - the reported symptom ("yesterday's data is gone") disappears while a subtler corruption is introduced, and the count that was already wrong stays wrong.
With the newline added as well, two runs produce 6 lines, 2 of which start with ERROR. The lesson is to test the observable property end to end (number of records, count per severity) after each change, rather than assuming that fixing the truncation fixed the reporting.