Extensible Logger With Independent Handlers and Any-Keyword Search Over Stored Logs
Company: Rippling
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: easy
Interview Round: Technical Screen
Build a logger that passes every logged message to a set of handlers. The handlers are independent of each other: each one receives the original message and does its own work with it, and no handler sees another handler's output. The logger must support four handler types:
- **Remove**: removes every occurrence of a configured string from the message, then prints the result.
- **Truncate**: truncates the message to its first `N` characters, then prints the result.
- **Uppercase**: converts the whole message to uppercase, then prints the result.
- **Store**: keeps the message in an internal array without printing anything.
For example, a logger with a remove handler configured with `"secret "`, a truncate handler with `N = 10`, an uppercase handler and a store handler, given the message `"user secret token ok"`, prints `user token ok`, `user secre` and `USER SECRET TOKEN OK`, and stores the original message.
### Constraints and Clarifications
- Messages are strings. One logged message is delivered to every handler attached to the logger.
- Part 2 searches only the messages kept by the store handler.
### Clarifying Questions
- Do handlers run in the order they were added, and must printed lines appear in that order?
- Can a logger have several handlers of the same type with different configurations, such as two truncate handlers with different `N`?
- If one handler fails on a message, should the remaining handlers still receive it?
### Part 1 — Handlers and an extensible design
Implement the logger and the four handler types. Discuss which design pattern fits, how a new handler type can be added without changing the logger, and how the design leaves room for future API use cases.
```hint What varies
The handler types differ only in what they do with one message. Find the single operation they all share, and what the logger needs to know about a handler beyond that.
```
```hint Transforming is not printing
Three of the handler types transform the text and then print it. Consider whether the transformation and the place the output goes should be the same object.
```
#### Clarifying Questions for this Part
- Should removal be a single left-to-right pass, or repeated until the configured string no longer occurs? Removing `"ab"` from `"aabb"` in one pass leaves `"ab"`.
- What should happen when the configured string is empty, or when `N` is zero or longer than the message?
- Which future use cases are expected: new handler types, output to destinations other than the console, log levels, or configuring handlers from a file or an API?
#### What This Part Should Cover
- A common handler interface, and a logger that depends only on that interface
- A named design pattern, justified against alternatives, and the steps to add a new handler type
- Correct behavior of each handler type, including edge cases of its configuration
- Room for future use cases such as other output destinations, levels or configuration-driven setup
### Part 2 — Search the stored messages
Add a search over the messages kept by the store handler. Given a list of keywords, for example `[x, y, z]`, return the stored messages that match ANY of the keywords, with no duplicates. Matching is case-sensitive. Messages contain no punctuation, and the words of each message are separated by spaces. Optimize time and space complexity.
```hint Where the work happens
Think about how the cost of one search grows with the number of stored messages, and whether some of that work could move to the moment a message is stored.
```
```hint One message, many keywords
A message can contain several of the keywords. Make sure the way you collect results returns it once, and in a predictable order.
```
#### Clarifying Questions for this Part
- Does a keyword match only a whole space-separated word, or also a substring inside a word?
- In what order should matching messages be returned?
- If the same text was stored twice as two separate log entries, should it appear once or twice in the results?
#### What This Part Should Cover
- Correct case-sensitive, any-keyword matching with duplicates removed and a defined result order
- The time and space complexity of storing and of searching, for a scan and for a structure built when messages are stored
- The memory the search structure adds compared with the stored messages
### Part 3 — Follow-ups (discussion only)
This part is discussed without code.
1. How would you handle messages that contain punctuation, and how would you support fuzzy matching of keywords?
2. Setting databases and caching aside, how would you optimize the logger itself for a production system?
3. What happens when there is a very large number of log messages, and how would you improve the design?
```hint Same steps on both sides
Once text contains punctuation, decide where it is turned into searchable words, and make sure stored messages and keywords go through exactly the same steps.
```
```hint The application is waiting
In production, every log call runs inside the application's own work. Separate what must happen before the call returns from what can happen later.
```
```hint Growth never stops
Follow what the store and its search structure hold after a week of heavy logging, and what removing old messages would cost.
```
#### What This Part Should Cover
- Consistent tokenization for punctuation, and a fuzzy matching method with its cost
- Keeping handler work off the caller's path, with thread safety and a policy for overload
- Bounded memory for very large volumes: retention, segmentation and compact storage
### What a Strong Answer Covers
- Independent, interchangeable handlers behind one interface, and a logger that is open for extension
- Correct handler semantics, including the edge cases of removal, truncation and case conversion
- A search that is correct on duplicates and order, with complexity stated for both storing and searching
- Trade-offs between search speed and memory, and between delivery guarantees and the caller's latency
- A production view: concurrency, failure isolation between handlers, and bounded growth
### Follow-up Questions
- One handler writes to a slow destination. How do you keep it from delaying the other handlers and the application?
- Search must now also return messages that contain ALL of the keywords. What changes in your data structure and its complexity?
- How would you let a caller remove a handler while other threads are logging?
Overview: An object-oriented design and coding question about a logger with independent handlers that strip a configured string, truncate, uppercase or store messages, followed by a case-sensitive any-keyword search over stored logs. It tests extensible design patterns, indexing for fast search, and scaling a logger to production volume.
Read the full Rippling Software Engineer interview experience this question came from