X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;ds=sidebyside;f=org.argeo.init%2Fsrc%2Forg%2Fargeo%2Finit%2Flogging%2FThinLogging.java;h=dd6fad2e3a5a54c261517b02f720cacf9f925278;hb=c7b612ca9bd7df43b0bec37c8abcae846587c978;hp=c2ce215288171bf94543a5fd0a949013b25b6bf2;hpb=d0e3d2246086746e73b2b68c9a6aaf8395ee93b7;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 c2ce21528..dd6fad2e3 100644 --- a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java @@ -1,9 +1,14 @@ package org.argeo.init.logging; +import java.io.IOException; import java.io.PrintStream; import java.io.Serializable; import java.lang.System.Logger; import java.lang.System.Logger.Level; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.MessageFormat; import java.time.Instant; import java.util.Collections; @@ -24,8 +29,8 @@ 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; +import org.argeo.api.init.RuntimeContext; +import org.argeo.internal.init.InternalState; /** * A thin logging system based on the {@link Logger} framework. It is a @@ -43,10 +48,12 @@ class ThinLogging implements Consumer> { */ final static String PROP_ARGEO_LOGGING_SYNCHRONOUS = "argeo.logging.synchronous"; /** - * Whether to enable jounrald compatible output, either: auto (default), true, + * Whether to enable journald compatible output, either: auto (default), true, * or false. */ final static String PROP_ARGEO_LOGGING_JOURNALD = "argeo.logging.journald"; + /** A file to which additionally write log entries. */ + final static String PROP_ARGEO_LOGGING_FILE = "argeo.logging.file"; /** * The level from which call location (that is, line number in Java code) will * be searched (default is WARNING) @@ -61,12 +68,13 @@ class ThinLogging implements Consumer> { // we don't synchronize maps on purpose as it would be // too expensive during normal operation // updates to the config may be shortly inconsistent - private SortedMap loggers = new TreeMap<>(); + private SortedMap loggers = Collections.synchronizedSortedMap(new TreeMap<>()); private NavigableMap levels = new TreeMap<>(); private volatile boolean updatingConfiguration = false; private final LogEntryPublisher publisher; private PrintStreamSubscriber synchronousSubscriber; + private PrintStream fileOut; private final boolean journald; private final Level callLocationLevel; @@ -82,6 +90,21 @@ class ThinLogging implements Consumer> { PrintStreamSubscriber subscriber = new PrintStreamSubscriber(); publisher = new LogEntryPublisher(); publisher.subscribe(subscriber); + String logFileStr = System.getProperty(PROP_ARGEO_LOGGING_FILE); + if (logFileStr != null) { + Path logFilePath = Paths.get(logFileStr); + if (!Files.exists(logFilePath.getParent())) { + System.err.println("Parent directory of " + logFilePath + " does not exist"); + } else { + try { + fileOut = new PrintStream(Files.newOutputStream(logFilePath), true, StandardCharsets.UTF_8); + publisher.subscribe(new PrintStreamSubscriber(fileOut, fileOut)); + } catch (IOException e) { + System.err.println("Cannot write log to " + logFilePath); + e.printStackTrace(); + } + } + } } Runtime.getRuntime().addShutdownHook(new Thread(() -> close(), "Log shutdown")); @@ -127,7 +150,7 @@ class ThinLogging implements Consumer> { } private void close() { - RuntimeContext runtimeContext = Service.getRuntimeContext(); + RuntimeContext runtimeContext = InternalState.getMainRuntimeContext(); if (runtimeContext != null) { try { runtimeContext.waitForStop(0); @@ -147,6 +170,14 @@ class ThinLogging implements Consumer> { // silent } } + + if (fileOut != null) { + try { + fileOut.close(); + } catch (Exception e) { + // silent + } + } } private Level computeApplicableLevel(String name) { @@ -160,9 +191,11 @@ class ThinLogging implements Consumer> { } public Logger getLogger(String name, Module module) { + Objects.requireNonNull(name, "logger name"); if (!loggers.containsKey(name)) { ThinLogger logger = new ThinLogger(name, computeApplicableLevel(name)); loggers.put(name, logger); + return logger; } return loggers.get(name); } @@ -362,11 +395,11 @@ 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": case "sun.util.logging.internal.LoggingProviderImpl$JULWrapper": + case "org.slf4j.impl.ArgeoLogger": + case "org.argeo.cms.internal.osgi.CmsOsgiLogger": + case "org.argeo.init.osgi.OsgiBootUtils": lowestLoggerInterface = i; continue stack; default: @@ -578,24 +611,24 @@ class ThinLogging implements Consumer> { } } - public static void main(String args[]) { - Logger logger = System.getLogger(ThinLogging.class.getName()); - logger.log(Logger.Level.ALL, "Log all"); - logger.log(Logger.Level.TRACE, "Multi\nline\ntrace"); - logger.log(Logger.Level.DEBUG, "Log debug"); - 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 - } - - } +// public static void main(String args[]) { +// Logger logger = System.getLogger(ThinLogging.class.getName()); +// logger.log(Logger.Level.ALL, "Log all"); +// logger.log(Logger.Level.TRACE, "Multi\nline\ntrace"); +// logger.log(Logger.Level.DEBUG, "Log debug"); +// 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 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 +// } +// +// } }