Design a Logger That Runs Each Message Through an Ordered List of Handlers
Company: Oracle
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: easy
Interview Round: Onsite
Design a small logging library in an object-oriented way. A `Logger` is created with a list of handlers, and every message passed to `log` must go through all of the handlers. Implement two handlers:
- `TruncateHandler`: shortens the message.
- `StoreHandler`: saves the message into an internal list instead of printing it.
The skeleton and the test provided:
```python
class Logger:
def __init__(self, handlers): # a list of handlers
...
def log(self, message):
# message should go through all handlers
...
class TruncateHandler:
...
class StoreHandler:
# save the message into an internal list instead of printing
...
# test
logger = Logger([TruncateHandler(), StoreHandler()])
logger.log('abc')
```
The code does not need to run. The focus is the class design, the contract between the logger and its handlers, and how you would test it.
```hint Fix the contract first
`Logger` should work with any handler without knowing its concrete type. Decide the single method every handler exposes, and what it hands back to the logger.
```
```hint Let the order matter on purpose
The test lists `TruncateHandler` before `StoreHandler`. Ask yourself what the stored list should contain if the two were swapped.
```
### Clarifying Questions
- Does each handler receive the output of the previous handler (a pipeline), or does every handler receive the original message?
- What length does `TruncateHandler` cut to? The test constructs it without arguments, so what should the default be, and should a truncated message carry a marker such as an ellipsis?
- "Instead of printing" suggests that other handlers print. Is a printing handler part of the expected design?
- Can a handler stop a message from reaching the handlers after it?
- How should a test read what `StoreHandler` has saved?
### What a Strong Answer Covers
- A handler abstraction with one method, so `Logger` depends only on that contract and new handlers need no change to `Logger`.
- Explicit ordering semantics: handlers run in list order, and it is stated whether each one sees the previous handler's output.
- A `TruncateHandler` with a configurable limit and defined behavior for short and empty messages, and a `StoreHandler` that exposes its messages to tests without leaking its internal list.
- Tests that pin down truncation, accumulation and the effect of handler order.
### Follow-up Questions
- How would you add log levels so that a handler processes only messages at or above its own level?
- If one handler raises an exception, should the remaining handlers still run, and should `log` raise?
- How would you make the logger safe to call from several threads, and should `StoreHandler` be bounded?
- How would you support structured messages (level, timestamp, fields) without breaking existing handlers?
Overview: Design a small object-oriented logging library in which a Logger passes every message through an ordered list of handlers, including one that truncates messages and one that stores them in memory instead of printing. It tests interface design, handler ordering, extensibility and testability.
Read the full Oracle Software Engineer interview experience this question came from