Please suggest how to expand on this low level logger implementation in Java

This question is asked in so many low level design rounds but I couldn't find any implementation that I could fully understand. So I decided to implement a very basic design from scratch and expand upon it. Here I've just used Singleton pattern and Observer pattern.

The Logger class is a singleton class and a subject (in observer context) which is having a list of panels/consoles that display different kind of logs. I know I am supposed to have an addObserver method here, but I've just added them in constructor for now.

public class Logger {

	private static final Logger instance = new Logger();
	private final List<IPanel> panelList;

	private Logger() {
		this.panelList = new ArrayList<>();
		panelList.add(new InfoPanel());
		panelList.add(new CriticalPanel());
	}

	public static Logger getInstance() {
		return instance;
	}

	public void log(Level level, String message) {
		for (IPanel panel : panelList) {
			if (panel.canPrint(level)) {
				panel.print(message);
			}
		}
	}
}

Different levels of type of logs, ERROR - most severe criticality.

public enum Level {
	INFO, DEBUG, ERROR
}

IPanel is the observer interface, which different type of consoles / panels can implement. The canPrint(Level level) method checks whether the respective panel should print a log or not based on the severity level.

public interface IPanel {
	void print(String message);
	boolean canPrint(Level level);
}

Two concrete panels implement the above.
CriticalPanel : only for error logs.

public class CriticalPanel implements IPanel {

	public void print(String message) {
		System.out.println("CRITICAL_PANEL " + System.currentTimeMillis() + " : " + message);
	}

	public boolean canPrint(Level level) {
		return level.ordinal() >= Level.ERROR.ordinal();
	}
}

InfoPanel : for all kind of logs.

public class InfoPanel implements IPanel{

	public void print(String message) {
		System.out.println("INFO_PANEL " + System.currentTimeMillis() + " : " + message);
	}

	public boolean canPrint(Level level) {
		return level.ordinal() >= Level.INFO.ordinal();
	}

}

Client that can use the above logger:

public class Main {

	private static final Logger logger = Logger.getInstance();

	public static void main(String[] args) {
		logger.log(Level.ERROR, "This is an error message");
		logger.log(Level.INFO, "This is an info message");
	}
}

I know it's not much but it's honest work. Can you please suggest how to exapnd on it and/or make it better? What areas to improve, like concurrency etc.?

Comments (1)