Designing a Logging Framework in Java Using Complete OOPS Principles
If you are a non-member, please use this link to read this article for free: click here
Designing a Logging Framework in Java Using Complete OOPS Principles
If you are a non-member, please use this link to read this article for free: click here
During a recent interview, I was asked to design a logging framework in Java using complete OOPS concepts. At first glance, this sounds simple — print messages with different log levels and output destinations.
But interviewers are not testing whether you can print to the console.
They are evaluating:
- Your understanding of OOPS principles
- Your knowledge of design patterns
- Your ability to write extensible and maintainable code
- Your thinking around scalability and clean architecture
In this article, I’ll walk through:
- Common mistakes developers make
- The right OOPS-based design
- How to make it interview-ready
- How to extend it like a production-grade system
The Common Mistake
Many developers start with something like:
- A
Loggerclass - Some hardcoded conditions
- String-based log levels
- Direct printing logic inside the logger
This approach usually:
- Violates Open/Closed Principle
- Hardcodes behavior
- Doesn’t use real polymorphism
- Fails to separate concerns
- Is not easily extensible
A logging framework should be:
- Extensible
- Configurable
- Cleanly abstracted
- Loosely coupled
Let’s design it properly.
Step 1: Define Log Levels (Use Enum)
Using strings for log levels is error-prone. Enums are safer and cleaner.
enum LogLevel {
DEBUG, INFO, WARN, ERROR
}
Why enum?
- Type safety
- Prevents invalid levels
- Enables natural severity comparison using
ordinal()
Step 2: Create Abstraction for Output Destination
This is where OOPS shines.
Instead of hardcoding console or file logic inside Logger, we define an interface.
interface LogWriter {
void write(String message);
}
This is abstraction.
The logger should not care how the message is written — only that it can be written.
Step 3: Implement Concrete Writers (Polymorphism)
Now we create multiple implementations.
Console Writer
class ConsoleWriter implements LogWriter {
@Override
public void write(String message) {
System.out.println("Console: " + message);
}
}
File Writer
class FileWriter implements LogWriter {
@Override
public void write(String message) {
System.out.println("File: " + message);
}
}
Now we have runtime polymorphism.
The Logger can work with any implementation of LogWriter.
Step 4: Design the Logger Class (Encapsulation + Strategy Pattern)
class Logger {
private LogLevel currentLevel;
private LogWriter writer;
public Logger(LogLevel level, LogWriter writer) {
this.currentLevel = level;
this.writer = writer;
}
public void log(LogLevel level, String message) {
if (level.ordinal() >= currentLevel.ordinal()) {
writer.write(level + ": " + message);
}
}
}
What’s happening here?
currentLevelis encapsulated (private)LogWriteris injected (Strategy Pattern)- Logging behaviour depends on severity
- No hardcoded output logic
This is clean OOPS design.
Step 5: Using the Logger
Output:
Console: ERROR: This will print
OOPS Concepts Demonstrated
1. Abstraction
LogWriter interface hides implementation details.
2. Encapsulation
Logger’s internal state is private.
3. Polymorphism
Multiple implementations of LogWriter.
4. Open/Closed Principle
We can add new writers without modifying Logger.
For example:
class DatabaseWriter implements LogWriter {
@Override
public void write(String message) {
// write to DB
}
}
No modification required in Logger.
Design Pattern Used: Strategy Pattern
The logging destination is injected dynamically.
Instead of:
if (type.equals("console")) { ... }
We delegate behaviour to strategy objects.
This makes the system:
- Flexible
- Testable
- Clean
How to Make It Even More Impressive in an Interview
If the interviewer pushes further, you can enhance it with:
1. Singleton Logger
Ensure only one logger instance exists.
2. Logger Factory
Create different logger configurations.
class LoggerFactory {
public static Logger getConsoleLogger(LogLevel level) {
return new Logger(level, new ConsoleWriter());
}
}
3. Thread Safety
Make logging synchronised or use a blocking queue.
4. Asynchronous Logging
Use a thread pool to avoid blocking the main thread.
5. Formatter Interface
Add customisable log formats:
interface LogFormatter {
String format(LogLevel level, String message);
}
Now your design becomes production-grade.
Now your design becomes production-grade.
Interview Insight
When an interviewer asks you to “implement a logging framework using OOPS,” they are not checking your ability to print strings.
They are checking whether you:
- Understand clean architecture
- Apply SOLID principles
- Use design patterns appropriately
- Write extensible systems
- Think beyond the immediate requirement
The difference between a beginner solution and a strong 3+ YOE solution is architectural thinking.
Final Thoughts
A logging framework is a simple problem on the surface, but a powerful way to demonstrate:
- Object-oriented design
- Design patterns
- Clean coding principles
- Extensibility mindset
If you can explain your design decisions clearly — not just write the code — you stand out immediately.
And that’s exactly what interviews are about.
Thanks for reading!!
If this article added value to your learning, please consider giving it a thumbs up 👍 It genuinely motivates me to keep writing and sharing more content like this.
메타데이터
- post_id
- 9040f53c19a8
- slug
- implementing-a-logging-framework-using-oops-concepts-9040f53c19a8
- url
- https://medium.com/@hritikarora1997/implementing-a-logging-framework-using-oops-concepts-9040f53c19a8
- canonical_url
- https://medium.com/@hritikarora1997/implementing-a-logging-framework-using-oops-concepts-9040f53c19a8
- author_url
- https://medium.com/@hritikarora1997
- status
- ok
- fetched_at
- 2026-08-03 09:09:56