]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - org.argeo.init/src/org/argeo/init/logging/ThinLogging.java
Switch thin logger to synchronous in order to support native images
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / logging / ThinLogging.java
index cdcabcb03060902a54a7698d08cc3ec49d51596a..05fdfef1af265c219268aeab389df6d9b4203715 100644 (file)
@@ -16,16 +16,17 @@ import java.util.ResourceBundle;
 import java.util.SortedMap;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Flow;
 import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.SubmissionPublisher;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 
+import org.argeo.init.RuntimeContext;
+import org.argeo.init.Service;
+
 /**
  * A thin logging system based on the {@link Logger} framework. It is a
  * {@link Consumer} of configuration, and can be registered as such.
@@ -48,24 +49,33 @@ class ThinLogging implements Consumer<Map<String, Object>> {
        private NavigableMap<String, Level> levels = new TreeMap<>();
        private volatile boolean updatingConfiguration = false;
 
-       private final ExecutorService executor;
+//     private final ExecutorService executor;
        private final LogEntryPublisher publisher;
+       private PrintStreamSubscriber synchronousSubscriber;
 
        private final boolean journald;
        private final Level callLocationLevel;
 
-       ThinLogging() {
-               executor = Executors.newCachedThreadPool((r) -> {
-                       Thread t = new Thread(r);
-                       t.setDaemon(true);
-                       return t;
-               });
-               publisher = new LogEntryPublisher(executor, Flow.defaultBufferSize());
+       private boolean synchronous = true;
 
-               PrintStreamSubscriber subscriber = new PrintStreamSubscriber();
-               publisher.subscribe(subscriber);
+       ThinLogging() {
+//             executor = Executors.newCachedThreadPool((r) -> {
+//                     Thread t = new Thread(r);
+//                     t.setDaemon(true);
+//                     return t;
+//             });
+               if (synchronous) {
+                       publisher = new LogEntryPublisher();
+                       synchronousSubscriber = new PrintStreamSubscriber();
+               } else {
+                       publisher = new LogEntryPublisher();
+
+                       PrintStreamSubscriber subscriber = new PrintStreamSubscriber();
+                       publisher.subscribe(subscriber);
+
+                       Runtime.getRuntime().addShutdownHook(new Thread(() -> close(), "Log shutdown"));
 
-               Runtime.getRuntime().addShutdownHook(new Thread(() -> close(), "Log shutdown"));
+               }
 
                // initial default level
                levels.put("", Level.WARNING);
@@ -115,11 +125,21 @@ class ThinLogging implements Consumer<Map<String, Object>> {
        }
 
        private void close() {
+               RuntimeContext runtimeContext = Service.getRuntimeContext();
+               if (runtimeContext != null) {
+                       try {
+                               runtimeContext.waitForStop(0);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+               }
+
                publisher.close();
                try {
                        // we ait a bit in order to make sure all messages are flushed
                        // TODO synchronize more efficiently
-                       executor.awaitTermination(300, TimeUnit.MILLISECONDS);
+                       // executor.awaitTermination(300, TimeUnit.MILLISECONDS);
+                       ForkJoinPool.commonPool().awaitTermination(300, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                        // silent
                }
@@ -274,6 +294,8 @@ class ThinLogging implements Consumer<Map<String, Object>> {
 
                        // NOTE: this is the method called when logging a plain message without
                        // exception, so it should be considered as a format only when args are not null
+                       if (format.contains("{}"))// workaround for weird Jetty formatting
+                               params = null;
                        String msg = params == null ? format : MessageFormat.format(format, params);
                        publisher.log(this, level, bundle, msg, now, thread, (Throwable) null, findCallLocation(level, thread));
                }
@@ -328,8 +350,8 @@ class ThinLogging implements Consumer<Map<String, Object>> {
 
        private class LogEntryPublisher extends SubmissionPublisher<Map<String, Serializable>> {
 
-               private LogEntryPublisher(Executor executor, int maxBufferCapacity) {
-                       super(executor, maxBufferCapacity);
+               private LogEntryPublisher() {
+                       super();
                }
 
                private void log(ThinLogger logger, Level level, ResourceBundle bundle, String msg, Instant instant,
@@ -358,7 +380,13 @@ class ThinLogging implements Consumer<Map<String, Object>> {
                        logEntry.put(KEY_THREAD, thread.getName());
 
                        // should be unmodifiable for security reasons
-                       submit(Collections.unmodifiableMap(logEntry));
+                       if (synchronous) {
+                               assert synchronousSubscriber != null;
+                               synchronousSubscriber.onNext(logEntry);
+                       } else {
+                               if (!isClosed())
+                                       submit(Collections.unmodifiableMap(logEntry));
+                       }
                }
 
        }
@@ -544,6 +572,16 @@ class ThinLogging implements Consumer<Map<String, Object>> {
                logger.log(Logger.Level.INFO, "Log info");
                logger.log(Logger.Level.WARNING, "Log warning");
                logger.log(Logger.Level.ERROR, "Log exception", new Throwable());
+
+               try {
+                       // we ait a bit in order to make sure all messages are flushed
+                       // TODO synchronize more efficiently
+                       // executor.awaitTermination(300, TimeUnit.MILLISECONDS);
+                       ForkJoinPool.commonPool().awaitTermination(300, TimeUnit.MILLISECONDS);
+               } catch (InterruptedException e) {
+                       // silent
+               }
+
        }
 
 }