All Downloads are FREE. Search and download functionalities are using the official Maven repository.

abs.api.LoggingRouter Maven / Gradle / Ivy

package abs.api;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * A logging {@link Router} implementation that creates a log of
 * actor messages in the format
 * 
 * 
 * TIME,FROM,TO,MESSAGE_ID
 * 
* * in which MESSAGE_ID is the object hash code of * the message passed. *

* The logging can be enabled by system property * {@link #JABS_LOGGING_ENABLED} and the path of the log file * can be configured via {@link #JABS_LOGGING_PATH}. By the * default, if enabled and no path is provided, a log file is * created in the Java Temp directory. */ public class LoggingRouter implements Router { /** * System property to determine if logging is enabled or not. */ public static final String JABS_LOGGING_ENABLED = "jabs.logging.enabled"; /** * The full path to the logging file for jabs */ public static final String JABS_LOGGING_PATH = "jabs.log.path"; static final String DEFAULT_LOG_PATH = System.getProperty("java.io.tmpdir") + "/jabs-log-" + System.currentTimeMillis() + ".log"; /** * When the system started. */ static final Instant TIME_ORIGIN = Instant.now(); static final class LoggingEvent { private final long time; private final String from; private final String to; private final String message; private final String toString; LoggingEvent(String from, String to, String message) { final Instant now = Instant.now(); this.time = now.toEpochMilli(); this.from = from; this.to = to; this.message = message; final long relativeTime = Duration.between(TIME_ORIGIN, now).toMillis(); this.toString = String.join(";", Long.toString(time), Long.toString(relativeTime), this.from, this.to, this.message); } @Override public String toString() { return toString; } } static final class LoggingThread extends Thread { private static final int BUFFER_FLUSH_SIZE = 8 * 1024; private static final long PERIOD = Duration.ofMillis(100).toMillis(); private final AtomicBoolean running = new AtomicBoolean(false); private final BlockingQueue events; private final Path logPath; LoggingThread(BlockingQueue events, Path logPath) { super("jabs-logging"); this.events = events; this.logPath = logPath; } @Override public void run() { if (!running.compareAndSet(false, true)) { throw new IllegalStateException("Cannot run while running is not enabled"); } while (running.get()) { try { Thread.sleep(PERIOD); } catch (InterruptedException e) { // Ignored } flush(); } flush(); } @Override public void interrupt() { running.getAndSet(false); } private void flush() { List buffer = new ArrayList<>(); fillBuffer(buffer, events); if (buffer.isEmpty()) { return; } writeBuffer(buffer, logPath); } private void writeBuffer(List buffer, Path path) { try (BufferedWriter w = Files.newBufferedWriter(path, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { List lines = buffer.stream().map(e -> e.toString()).collect(Collectors.toList()); lines.forEach(line -> { try { w.write(line); w.newLine(); } catch (Exception ignored) { // Ignored } }); } catch (IOException e) { // Ignored } } private void fillBuffer(List buffer, BlockingQueue events) { if (events.size() <= BUFFER_FLUSH_SIZE) { buffer.addAll(events); events.clear(); } else { for (int i = 0; i < BUFFER_FLUSH_SIZE; ++i) { LoggingEvent e = events.poll(); if (e == null) { break; } buffer.add(e); } } } } private final BlockingQueue events = new LinkedBlockingQueue<>(); private final boolean enabled; /** * Ctor. * * @see #LoggingRouter(boolean, String) */ public LoggingRouter() { this(Boolean.getBoolean(JABS_LOGGING_ENABLED), System.getProperty(JABS_LOGGING_PATH, DEFAULT_LOG_PATH)); } /** * Ctor * * @param enabled if the logging is enabled */ LoggingRouter(final boolean enabled) { this(enabled, DEFAULT_LOG_PATH); } /** * Ctor * * @param enabled if the logging is enabled * @param logFilePath the full path to the log file */ public LoggingRouter(final boolean enabled, String logFilePath) { this.enabled = enabled; if (!enabled) { return; } try { LoggingThread lt = new LoggingThread(events, Paths.get(logFilePath)); lt.start(); } catch (Exception e) { // Ignore } } @Override public void route(Envelope envelope) { if (!this.enabled) { return; } LoggingEvent event = create(envelope); events.offer(event); } private LoggingEvent create(Envelope e) { String from = e.from() == null ? "NOBODY" : e.from().simpleName(); String to = e.to().simpleName(); String message = toString(e.message()); return new LoggingEvent(from, to, message); } @Override public void bind(Context context) {} protected String toString(Object o) { if (o == null) { return "null"; } final String hashCode = "@" + Integer.toHexString(o.hashCode()); if (o.toString().contains("Lambda")) { return "Msg" + hashCode; } if (o instanceof Runnable || o instanceof Callable) { return o.getClass().getSimpleName() + hashCode; } return "Msg" + hashCode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy