Extensible Logger With Independent Handlers and Any-Keyword Search Over Stored Logs
RipplingSoftware Engineer · Technical ScreenLast updated
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.
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
Ncharacters, 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.
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
Nis 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.
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.
- How would you handle messages that contain punctuation, and how would you support fuzzy matching of keywords?
- Setting databases and caching aside, how would you optimize the logger itself for a production system?
- What happens when there is a very large number of log messages, and how would you improve the design?
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
Hints
Hint 1
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 2
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.
Hint 3
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 4
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.
Hint 5
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 6
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 7
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.
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?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.