Skip to main content

Getting Ready: Logging Framework

Getting Ready: Logging Framework

Why This Problem Matters

Every production system in the world logs. When a payment fails at Stripe, an engineer opens the logs. When a request times out at Netflix, the first diagnostic step is checking the logs. When a Kubernetes pod crashes, the cluster's logging pipeline captures the dying breath of the container before it's recycled. Logging is the nervous system of software — invisible when things go well, indispensable when they don't.

But logging frameworks are more than print() statements. A real logging framework must solve several non-trivial engineering problems simultaneously:

  • Level filtering — in production, you want ERROR and WARN messages but not DEBUG chatter. In development, you want everything. The framework must filter messages by severity without changing application code.
  • Multiple output destinations (sinks) — a single log message might need to go to the console for developer convenience, a file for persistent storage, and a remote database or service like Datadog, Splunk, or the ELK stack for centralized monitoring.
  • Extensibility — when your company adopts a new monitoring tool next quarter, adding a new log sink should not require modifying the existing logger code. This is the Open/Closed Principle in action.
  • Global access — loggers are used everywhere: controllers, services, repositories, utilities. The framework must be globally accessible without passing a logger instance through every method signature.

Real-world logging frameworks that solve these problems include:

  • Python's logging module — built into the standard library, uses handlers (sinks), formatters, and level filtering
  • Java's Log4j / SLF4J / Logback — the de facto standard in the Java ecosystem, with appenders for console, file, database, and network destinations
  • Winston (Node.js) — transport-based architecture where each transport is an output destination
  • Serilog (.NET) — structured logging with sink-based output routing

All of these share the same foundational design: a central logger that filters by level and dispatches to pluggable sinks. This is precisely what we'll build.

In LLD interviews, the Logging Framework is a favorite because it naturally exercises three important design patterns — Singleton, Chain of Responsibility, and Observer — in a system that every developer intuitively understands. Interviewers love it because it tests whether you can design for extensibility (adding new sinks) without overengineering.

What You'll Learn

In this design problem, you will:

  • Apply the Singleton Pattern — ensure only one Logger instance exists application-wide, providing a global access point without polluting method signatures
  • Apply the Chain of Responsibility Pattern — route log messages through a chain of level-based filters, where each handler decides whether to process or pass the message along
  • Apply the Observer Pattern — decouple the logger from its output destinations so that multiple sinks receive and process log messages independently
  • Design for the Open/Closed Principle — structure the system so adding a new log sink (e.g., Slack notifications, Datadog integration) requires zero modification to existing code
  • Model the system with a UML class diagram — identify classes, relationships, and the role of each design pattern in the structure
  • Implement in Python and Java — write complete, compilable logging framework code in both languages
  • Test edge cases — handle concurrent logging, empty sink lists, invalid log levels, and sink failures gracefully

Prerequisites

Before starting this problem, make sure you're comfortable with:

  • OOP Fundamentals — classes, encapsulation, inheritance, and polymorphism (Fundamentals S1-S3). The Logging Framework relies heavily on polymorphism for interchangeable sinks
  • OOP Relationships — especially Association and Composition (Fundamentals S4). The Logger associates with multiple LogSink objects
  • Interfaces vs Abstract Classes — you'll define a LogSink interface that concrete sinks implement (Fundamentals S4)
  • UML Class Diagrams — how to read class boxes, interfaces, and relationship arrows (Fundamentals S5)
  • Open/Closed Principle — the 'O' in SOLID (Fundamentals S6). This problem is one of the clearest demonstrations of OCP — new sinks are added by implementing an interface, not by modifying the logger
  • Singleton Pattern — global instance with controlled creation (Fundamentals S10). You should understand both the basic and thread-safe versions
  • Observer Pattern — one-to-many notification (Fundamentals S12). The logger notifies all registered sinks when a message passes the level filter
  • Chain of Responsibility Pattern — sequential delegation through a chain of handlers (Fundamentals S12). Used for level-based filtering

If you've completed the Parking Lot (DP1), Tic Tac Toe (DP2), Snake and Ladder (DP3), and LRU Cache (DP4) problems, you're well-prepared. This problem introduces a new challenge: designing infrastructure-level software rather than domain-specific systems.

Problem Scope

We will design: The core logging framework — a Logger class that accepts log messages at different severity levels, filters them based on a configured minimum level, and dispatches matching messages to one or more pluggable sinks (Console, File, Database). We'll ensure the Logger is a Singleton and that adding new sinks requires no changes to existing code.

We will NOT design: Log aggregation pipelines (like the ELK stack), distributed tracing, log rotation/archival policies, structured logging (JSON output), network transport protocols, or monitoring dashboards. This is a Low Level Design (object-oriented) exercise focused on the framework's internal architecture, not a System Design (infrastructure) problem.

Difficulty: Easy — The Logging Framework has a small number of classes (6-8) and straightforward relationships. What makes it an excellent learning exercise is that it naturally showcases three distinct design patterns working together in a cohesive system. The extensibility story is clean and compelling.