Functional Scope (In-Scope)
- Pluggable LogLevels: Support flexible severity levels (DEBUG, INFO, WARN, ERROR, FATAL) systematically.
- Multiple Sinks Output: Enable swappable output sinks (Console, local File log records, Remote database logs) polymorphically.
- Non-Blocking Async Buffer Queues: Call logs in non-blocking manner using bounded consumer writing queues.
- Extensible Formatting: Strategy formats supporting JSON as well as Text formatting configurations.
- Graceful Shutdown Flushing: Drain log buffers completely on execution completion before shutdown.
Explicit Boundaries (Out-of-Scope)
- No Real Network Call Sockets: Remote writing steps utilize mock endpoints rather than raw socket transports.
- No Complex Log Rotation Scheduling: Overwrite/cleanup strategies for local logs are out-of-scope.
Thread-safe async logging setups in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
enum LogLevel { DEBUG, INFO, WARN, ERROR, FATAL }
class LogRecord {
private final long timestamp;
private final LogLevel level;
private final String message;
private final String threadName;
private final String loggerName;
public LogRecord(String loggerName, LogLevel level, String message) {
this.timestamp = System.currentTimeMillis();
this.loggerName = loggerName;
this.level = level;
this.message = message;
this.threadName = Thread.currentThread().getName();
}
public long getTimestamp() { return timestamp; }
public LogLevel getLevel() { return level; }
public String getMessage() { return message; }
public String getThreadName() { return threadName; }
public String getLoggerName() { return loggerName; }
}
interface LogFormatter {
String format(LogRecord record);
}
class TextFormatter implements LogFormatter {
@Override
public String format(LogRecord record) {
return String.format("[%d] [%s] [%s] [%s] - %s",
record.getTimestamp(),
record.getLevel(),
record.getLoggerName(),
record.getThreadName(),
record.getMessage());
}
}
class JsonFormatter implements LogFormatter {
@Override
public String format(LogRecord record) {
return String.format("{"timestamp":%d, "level":"%s", "logger":"%s", "thread":"%s", "message":"%s"}",
record.getTimestamp(),
record.getLevel(),
record.getLoggerName(),
record.getThreadName(),
record.getMessage().replace(""", "\""));
}
}
interface LogSink {
void write(LogRecord record, LogFormatter formatter);
}
class ConsoleSink implements LogSink {
@Override
public void write(LogRecord record, LogFormatter formatter) {
System.out.println("[Console] " + formatter.format(record));
}
}
class FileSink implements LogSink {
private final List<String> lines = new CopyOnWriteArrayList<>();
@Override
public void write(LogRecord record, LogFormatter formatter) {
String logLine = formatter.format(record);
lines.add(logLine);
// Simulate writing to physical disk
System.out.println("[FileSink Written] " + logLine);
}
}
class AsyncLogManager {
private final BlockingQueue<LogRecord> queue = new LinkedBlockingQueue<>(1000);
private final List<LogSink> sinks = new CopyOnWriteArrayList<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final LogFormatter formatter;
private volatile boolean running = true;
public AsyncLogManager(LogFormatter formatter) {
this.formatter = formatter;
executor.submit(this::processLogs);
}
public void addSink(LogSink sink) { sinks.add(sink); }
public void log(LogRecord record) {
if (!running) return;
if (!queue.offer(record)) {
System.err.println("Logger buffer overflow! Log message dropped: " + record.getMessage());
}
}
private void processLogs() {
while (running || !queue.isEmpty()) {
try {
LogRecord record = queue.poll(100, TimeUnit.MILLISECONDS);
if (record != null) {
for (LogSink sink : sinks) {
sink.write(record, formatter);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public void shutdown() {
running = false;
executor.shutdown();
try {
executor.awaitTermination(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Logger {
private final String name;
private final LogLevel minLevel;
private final AsyncLogManager manager;
public Logger(String name, LogLevel minLevel, AsyncLogManager manager) {
this.name = name;
this.minLevel = minLevel;
this.manager = manager;
}
public void log(LogLevel level, String message) {
if (level.ordinal() >= minLevel.ordinal()) {
manager.log(new LogRecord(name, level, message));
}
}
public void debug(String msg) { log(LogLevel.DEBUG, msg); }
public void info(String msg) { log(LogLevel.INFO, msg); }
public void warn(String msg) { log(LogLevel.WARN, msg); }
public void error(String msg) { log(LogLevel.ERROR, msg); }
public void fatal(String msg) { log(LogLevel.FATAL, msg); }
}
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== RUNNING LOGGER JAVA SIMULATION ===");
LogFormatter textFormat = new TextFormatter();
AsyncLogManager manager = new AsyncLogManager(textFormat);
manager.addSink(new ConsoleSink());
manager.addSink(new FileSink());
Logger sysLog = new Logger("SystemController", LogLevel.INFO, manager);
// Spawn multiple concurrent logging threads
ExecutorService concurrentPublishers = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 3; i++) {
final int id = i;
concurrentPublishers.submit(() -> {
sysLog.info("Log message from thread agent #" + id);
sysLog.debug("This is a debug log (should be ignored by INFO minimum level setup)");
sysLog.warn("Warning trace from thread agent #" + id);
});
}
concurrentPublishers.shutdown();
concurrentPublishers.awaitTermination(1, TimeUnit.SECONDS);
Thread.sleep(500); // Wait for background worker processing
manager.shutdown();
}
}