Fix packaged launch
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / logging / ThinLogging.java
index cdcabcb03060902a54a7698d08cc3ec49d51596a..85ed9eb4fe1a79e24dd4efa92be26f532ddd878b 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,19 @@ 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;
+                       // TODO move this to slf4j wrapper?
+                       if (format.contains("{}")) {
+                               StringBuilder sb = new StringBuilder();
+                               String[] segments = format.split("\\{\\}");
+                               for (int i = 0; i < segments.length; i++) {
+                                       sb.append(segments[i]);
+                                       if (i != (segments.length - 1))
+                                               sb.append("{" + i + "}");
+                               }
+                               format = sb.toString();
+                       }
                        String msg = params == null ? format : MessageFormat.format(format, params);
                        publisher.log(this, level, bundle, msg, now, thread, (Throwable) null, findCallLocation(level, thread));
                }
@@ -301,6 +334,7 @@ class ThinLogging implements Consumer<Map<String, Object>> {
                                        case "org.osgi.service.log.Logger":
                                        case "org.eclipse.osgi.internal.log.LoggerImpl":
                                        case "org.argeo.api.cms.CmsLog":
+                                       case "org.argeo.init.osgi.OsgiBootUtils":
                                        case "org.slf4j.impl.ArgeoLogger":
                                        case "org.argeo.cms.internal.osgi.CmsOsgiLogger":
                                        case "org.eclipse.jetty.util.log.Slf4jLog":
@@ -328,8 +362,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 +392,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 +584,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
+               }
+
        }
 
 }