X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.init%2Fsrc%2Forg%2Fargeo%2Finit%2Flogging%2FThinLogging.java;h=5b2c93924505112c9d4f4bade1ac7b6fcf88152f;hb=d2bca81ff63496bf1d879f4cbcd6a531f598e69c;hp=05fdfef1af265c219268aeab389df6d9b4203715;hpb=a960f73a1894dd035bacf11130fd5f9acb93f476;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java index 05fdfef1a..5b2c93924 100644 --- a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java @@ -37,8 +37,24 @@ class ThinLogging implements Consumer> { final static String DEFAULT_LEVEL_PROPERTY = "log"; final static String LEVEL_PROPERTY_PREFIX = DEFAULT_LEVEL_PROPERTY + "."; - final static String JOURNALD_PROPERTY = "argeo.logging.journald"; - final static String CALL_LOCATION_PROPERTY = "argeo.logging.callLocation"; + /** + * Whether logged event are only immediately printed to the standard output. + * Required for native images. + */ + final static String PROP_ARGEO_LOGGING_SYNCHRONOUS = "argeo.logging.synchronous"; + /** + * Whether to enable jounrald compatible output, either: auto (default), true, + * or false. + */ + final static String PROP_ARGEO_LOGGING_JOURNALD = "argeo.logging.journald"; + /** + * The level from which call location (that is, line number in Java code) will + * be searched (default is WARNING) + */ + final static String PROP_ARGEO_LOGGING_CALL_LOCATION_LEVEL = "argeo.logging.callLocationLevel"; + + final static String ENV_INVOCATION_ID = "INVOCATION_ID"; + final static String ENV_GIO_LAUNCHED_DESKTOP_FILE_PID = "GIO_LAUNCHED_DESKTOP_FILE_PID"; private final static AtomicLong nextEntry = new AtomicLong(0l); @@ -49,52 +65,37 @@ class ThinLogging implements Consumer> { private NavigableMap levels = new TreeMap<>(); private volatile boolean updatingConfiguration = false; -// private final ExecutorService executor; private final LogEntryPublisher publisher; private PrintStreamSubscriber synchronousSubscriber; private final boolean journald; private final Level callLocationLevel; - private boolean synchronous = true; + private final boolean synchronous; ThinLogging() { -// executor = Executors.newCachedThreadPool((r) -> { -// Thread t = new Thread(r); -// t.setDaemon(true); -// return t; -// }); + publisher = new LogEntryPublisher(); + synchronous = Boolean.parseBoolean(System.getProperty(PROP_ARGEO_LOGGING_SYNCHRONOUS, "false")); 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); + levels.put(DEFAULT_LEVEL_NAME, Level.WARNING); // Logging system config // journald - -// Map env = new TreeMap<>(System.getenv()); -// for (String key : env.keySet()) { -// System.out.println(key + "=" + env.get(key)); -// } - - String journaldStr = System.getProperty(JOURNALD_PROPERTY, "auto"); + String journaldStr = System.getProperty(PROP_ARGEO_LOGGING_JOURNALD, "auto"); switch (journaldStr) { case "auto": - String systemdInvocationId = System.getenv("INVOCATION_ID"); + String systemdInvocationId = System.getenv(ENV_INVOCATION_ID); if (systemdInvocationId != null) {// in systemd - // check whether we are indirectly in a desktop app (e.g. eclipse) - String desktopFilePid = System.getenv("GIO_LAUNCHED_DESKTOP_FILE_PID"); + // check whether we are indirectly in a desktop app (typically an IDE) + String desktopFilePid = System.getenv(ENV_GIO_LAUNCHED_DESKTOP_FILE_PID); if (desktopFilePid != null) { Long javaPid = ProcessHandle.current().pid(); if (!javaPid.toString().equals(desktopFilePid)) { @@ -117,10 +118,10 @@ class ThinLogging implements Consumer> { break; default: throw new IllegalArgumentException( - "Unsupported value '" + journaldStr + "' for property " + JOURNALD_PROPERTY); + "Unsupported value '" + journaldStr + "' for property " + PROP_ARGEO_LOGGING_JOURNALD); } - String callLocationStr = System.getProperty(CALL_LOCATION_PROPERTY, Level.WARNING.getName()); + String callLocationStr = System.getProperty(PROP_ARGEO_LOGGING_CALL_LOCATION_LEVEL, Level.WARNING.getName()); callLocationLevel = Level.valueOf(callLocationStr); } @@ -134,42 +135,29 @@ class ThinLogging implements Consumer> { } } - 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); - ForkJoinPool.commonPool().awaitTermination(300, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // silent + if (!synchronous) { + publisher.close(); + try { + // we wait 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 + } } } private Level computeApplicableLevel(String name) { Map.Entry entry = levels.floorEntry(name); assert entry != null; - return entry.getValue(); + if (name.startsWith(entry.getKey())) + return entry.getValue(); + else + return levels.get(DEFAULT_LEVEL_NAME);// default } -// private boolean isLoggable(String name, Level level) { -// Objects.requireNonNull(name); -// Objects.requireNonNull(level); -// -// if (updatingConfiguration) { -// synchronized (levels) { -// try { -// levels.wait(); -// // TODO make exit more robust -// } catch (InterruptedException e) { -// throw new IllegalStateException(e); -// } -// } -// } -// -// return level.getSeverity() >= computeApplicableLevel(name).getSeverity(); -// } - public Logger getLogger(String name, Module module) { if (!loggers.containsKey(name)) { ThinLogger logger = new ThinLogger(name, computeApplicableLevel(name)); @@ -222,7 +210,6 @@ class ThinLogging implements Consumer> { updatingConfiguration = false; levels.notifyAll(); } - } Flow.Publisher> getLogEntryPublisher() { @@ -294,8 +281,19 @@ class ThinLogging implements Consumer> { // 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; +// 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)); } @@ -323,6 +321,7 @@ class ThinLogging implements Consumer> { 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": @@ -433,6 +432,8 @@ class ThinLogging implements Consumer> { private PrintStream err; private int writeToErrLevel = Level.WARNING.getSeverity(); + private Subscription subscription; + protected PrintStreamSubscriber() { this(System.out, System.err); } @@ -448,7 +449,8 @@ class ThinLogging implements Consumer> { @Override public void onSubscribe(Subscription subscription) { - subscription.request(Long.MAX_VALUE); + this.subscription = subscription; + this.subscription.request(1); } @Override @@ -459,6 +461,8 @@ class ThinLogging implements Consumer> { out.print(toPrint(item)); } // TODO flush for journald? + if (this.subscription != null) + this.subscription.request(1); } @Override @@ -520,10 +524,9 @@ class ThinLogging implements Consumer> { protected String toPrint(Map logEntry) { StringBuilder sb = new StringBuilder(); StringTokenizer st = new StringTokenizer((String) logEntry.get(KEY_MSG), "\r\n"); - assert st.hasMoreTokens(); // first line - String firstLine = st.nextToken(); + String firstLine = st.hasMoreTokens() ? st.nextToken() : ""; sb.append(firstLinePrefix(logEntry)); sb.append(firstLine); sb.append(firstLineSuffix(logEntry));