The Log Pulse

An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.

Domain
Python
Difficulty
easy
Seniority
L3
Asked in
Amazon

Problem

Lines from the monitoring pipeline are shaped like [LEVEL] message, but some arrive empty, missing their brackets, or otherwise garbled, and the on-call engineer reviewing a batch files every one of those under 'UNKNOWN'. Report how many times each level appears alongside the most frequent level, giving a tie to whichever level shows up first in log_lines; an empty batch comes back as [{}, ''].

Some lines repeat themselves.

Example 1

Input:

log_lines = ["[ERROR] Connection timeout","[INFO] Server started","[ERROR] Disk full","[WARN] High memory","[INFO] Request handled","[INFO] Cache hit"]

Output:

[{"ERROR":2,"INFO":3,"WARN":1},"INFO"]

Example 2

Input:

log_lines = ["[INFO] startup","[ERROR] failure","[ERROR] crash","[INFO] request"]

Output:

[{"INFO":2,"ERROR":2},"INFO"]

Worked solution and explanation

Why this problem exists in real interviews

This looks like a counting exercise, and the counting is the easy half. The real test is the two things the happy path hides: malformed lines that crash a careless parser, and a tie rule where the level seen first wins. Anchor on the wrong character and a bracket-less line throws IndexError. Sort the levels alphabetically before picking the max and a batch where INFO showed up first hands the tie to ERROR. Either way you confidently report the wrong level on data that looked clean.


Break down the requirements

Step 1: Extract the level token from each line

Each line looks like '[LEVEL] message'. Find the first ], insist the line opens with [, and strip the padding between them so '[ DEBUG ]' counts as DEBUG. Keying on the first ] keeps '[INFO] [retry] reconnect' an INFO line. Anything that fails (no ], no leading [, an empty '[]') goes to 'UNKNOWN' instead of crashing or vanishing. A naive split that takes token zero skips this guard, and the dirty batch punishes exactly that.

Step 2: Tally with Counter

collections.Counter is built for this: one pass to build, constant-time increments, and a most_common helper. Indexing a missing key returns 0, so counts[level] += 1 needs no setup.

Step 3: Pick the most common level deterministically

Counter.most_common(1) returns the highest-count item, and ties keep insertion order, so the level that appeared first in the batch wins. That matches the rule. Note it is first appearance, not first to reach the top count: in '[INFO], [ERROR], [ERROR], [INFO]' ERROR hits two first, yet INFO wins. Re-sorting the keys before taking the max throws that order away.

Step 4: Return a fixed-shape list, not a tuple

The result is a fixed-shape list [counts_dict, most_common_level]. Return a plain list (not a tuple) and a plain dict (not a Counter), with the empty batch short-circuiting to [{}, ''] before most_common is touched.


The solution

Counter plus `most_common` does the work
from collections import Counter

def count_log_levels(log_lines: list[str]) -> list:
    counts = Counter()
    for line in log_lines:
        try:
            end = line.index(']')
            if not line.startswith('['):
                raise ValueError
            level = line[1:end].strip()
            if not level:
                raise ValueError
        except (ValueError, AttributeError):
            level = 'UNKNOWN'
        counts[level] += 1
    if not counts:
        return [{}, '']
    most_common_level = counts.most_common(1)[0][0]
    return [dict(counts), most_common_level]

Common follow-up questions