I believe an alternative to synchronizing the whole thing is to use an async approach. Let's say that all of the log entries are added to a BlockingQueue
and some other thread consumes the queue. Then, no synchronization is needed.
Example:
public class LogAsync {
// Some kind of abstraction for a log entry
public static class LogEntry {
private final String event;
private final long uniqueId;
public LogEntry(long uniqueId, String event) {
this.uniqueId = uniqueId;
this.event = event;
}
public String getEvent() {
return event;
}
public long getUniqueId() {
return uniqueId;
}
}
// A blocking queue where the entries are stored
private final BlockingQueue<LogEntry> logEvents = new LinkedBlockingQueue<>();
// Adds a log entry to the blocking queue
public void logUser(long uniqueID, String event) {
logEvents.add(new LogEntry(uniqueID, event));
}
// Starts the thread that handles the "writing to file"
public LogAsync start() {
// Run in another thread
CompletableFuture.runAsync(() -> {
while (true) {
try {
final LogEntry entry = logEvents.take();
try (BufferedWriter buffWriter = new BufferedWriter(new FileWriter(entry.getUniqueId() + ".log", true))) {
buffWriter.write(entry.getEvent());
} catch (IOException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
break;
}
}
}
);
return this;
}
}
The way you could initialize the whole thing could be like this:
final LogAsync logger = new LogAsync().start();
logger.logUser(1, "Hello");
logger.logUser(1, "there");
logger.logUser(2, "Goodbye");
So, this is basically an alternative to synchronizing the whole thing. Note that this code is sample code only and there are some modifications that must be taken to make production-worthy. E.g., there must be a nice way to shutdown the writer-thread.
However, my recommendation is to not use a synchronized or asynchronous approach. Instead, use a logging framework such as SLF4J.