Try Our Course for 4 Hours @99rs

Try our course for 4 Hours and if you like it, you can go for one year or lifetime access. If you buy our (1yr or lifetime) course 99rs will be refunded !

Design Logging library like log4j - Machine Coding

Design Logging library like log4j - Machine Coding

Features Required

  1. Logging Levels:
    Logging should support multiple levels like DEBUG, INFO, WARN, ERROR, and FATAL.

    • Helps filter messages based on severity.

    • Ensures flexibility to control verbosity.

  2. High Performance:

    • Must handle 1 million concurrent logs per second, making asynchronous logging and buffering mandatory.
  3. Appenders:

    • ConsoleAppender: Logs to the console.

    • FileAppender: Logs to a file.

    • RollingFileAppender: Supports log rotation based on size or time.

  4. Thread-Safety:
    Logs should not get mixed up in a multi-threaded environment.

  5. Asynchronous Logging:

    • Use a queue to buffer logs.

    • Worker threads consume from the queue for actual writing.

  6. Formatter/Layouts:
    Support customizable message formats like:
    [Timestamp] [LogLevel] [Thread] - Message

  7. Configuration:

    • Provide configuration via a JSON file.

    • Dynamically update logging behavior during runtime.

  8. Filters:
    Allow conditional logging (e.g., only log messages containing specific keywords).

Design Patterns Involved

  1. Singleton Pattern:

    • Ensure only one instance of the LogManager exists throughout the application.

    • Centralized logger configuration and management.

  2. Factory Pattern:

    • Dynamically create different types of Appender (e.g., ConsoleAppender, FileAppender).
  3. Observer Pattern:

    • Allow Appenders to observe log events from Logger.
  4. Builder Pattern:

    • Configure the Logger and its Appenders in a flexible and readable way.

Multiple Algorithms Involved

Covered in Course

Solution (Java)

public enum LogLevel {
    DEBUG, INFO, WARN, ERROR, FATAL;
}
import java.util.ArrayList;
import java.util.List;

public class Logger {
    private String name;
    private LogLevel level;
    private List<Appender> appenders = new ArrayList<>();

    public Logger(String name, LogLevel level) {
        this.name = name;
        this.level = level;
    }

    public void log(LogLevel level, String message) {
        if (level.ordinal() >= this.level.ordinal()) {
            for (Appender appender : appenders) {
                appender.append(formatMessage(level, message));
            }
        }
    }

    public void debug(String message) {
        log(LogLevel.DEBUG, message);
    }

    public void info(String message) {
        log(LogLevel.INFO, message);
    }

    public void warn(String message) {
        log(LogLevel.WARN, message);
    }

    public void error(String message) {
        log(LogLevel.ERROR, message);
    }

    public void fatal(String message) {
        log(LogLevel.FATAL, message);
    }

    private String formatMessage(LogLevel level, String message) {
        return String.format("[%s] [%s] [%s] %s",
                new java.util.Date(), level, Thread.currentThread().getName(), message);
    }

    public void addAppender(Appender appender) {
        appenders.add(appender);
    }
}
public interface Appender {
    void append(String message);
}
public class ConsoleAppender implements Appender {
    @Override
    public void append(String message) {
        System.out.println(message);
    }
}
import java.io.FileWriter;
import java.io.IOException;

public class FileAppender implements Appender {
    private FileWriter writer;

    public FileAppender(String filePath) throws IOException {
        this.writer = new FileWriter(filePath, true);
    }

    @Override
    public void append(String message) {
        try {
            writer.write(message + "\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.util.HashMap;
import java.util.Map;

public class LogManager {
    private static LogManager instance;
    private Map<String, Logger> loggers = new HashMap<>();

    private LogManager() {}

    public static synchronized LogManager getInstance() {
        if (instance == null) {
            instance = new LogManager();
        }
        return instance;
    }

    public Logger getLogger(String name) {
        return loggers.computeIfAbsent(name, key -> new Logger(name, LogLevel.INFO));
    }
}
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class AsyncAppender implements Appender {
    // will be covered in our course with video explanation
}
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.List;

public class ConfigLoader {
    // will be covered in our course with video explanation
}
public class AppenderFactory {
    public static Appender createAppender(AppenderConfig config) {
        switch (config.getType()) {
            case "ConsoleAppender":
                return new ConsoleAppender();
            case "FileAppender":
                try {
                    return new FileAppender(config.getFilePath());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            default:
                throw new IllegalArgumentException("Unknown Appender type: " + config.getType());
        }
    }
}
public class Main {
    public static void main(String[] args) {
        LogManager logManager = LogManager.getInstance();
        Logger logger = logManager.getLogger("MainLogger");

        logger.addAppender(new ConsoleAppender());
        try {
            logger.addAppender(new FileAppender("application.log"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        logger.info("This is an info message");
        logger.error("This is an error message");
    }
}

Some Class Code is Missing, those will be covered in our course with video explanation.