如何通过属性配置 java.util.logging 以使用标准输出?

meg*_*off 3 java logging properties

如何通过属性配置 java.util.logging 以使用标准输出而不是标准错误?

我当前的财产文件

# Logging
handlers = java.util.logging.ConsoleHandler
# Console Logging
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter =  java.util.logging.SimpleFormatter 
java.util.logging.SimpleFormatter.format = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n
Run Code Online (Sandbox Code Playgroud)

Ali*_*vei 6

从<logger>.handlers属性派生新的处理程序java.util.logging.StreamHandler并使用其\n完全限定名称。

\n\n

(如果使用 Apache Maven,请注意使用 Surefire 插件作为IPC及其分叉子项stdout的手段,因此在测试期间会出现损坏警告,请参阅此处。)

\n\n

例如(Java 9+)...

\n\n

布局目录:

\n\n
mkdir -p /tmp/logger/{src/org.foo/{{classes,tests}/org/foo/logging{,/internal},resources},{build/modules/org.foo,dist}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

验证布局:

\n\n
cd /tmp/logger && gio tree --hidden\n
Run Code Online (Sandbox Code Playgroud)\n\n
文件:///tmp/logger\n|-- 构建\n| `-- 模块\n| `-- org.foo\n|-- dist\n`-- src\n `-- org.foo\n |-- 类\n | `-- 组织\n | `-- foo\n | `-- 日志记录\n | `-- 内部\n |-- 资源\n `-- 测试\n `-- org\n `-- foo\n `-- 日志记录\n `-- 内部\n
\n\n

在分支下写类src/org.foo/classes

\n\n

一个处理程序。

\n\n
package org.foo.logging.internal;\n\nimport java.io.FileDescriptor;\nimport java.io.FileOutputStream;\nimport java.util.Objects;\n\nimport java.util.logging.Formatter;\nimport java.util.logging.LogRecord;\nimport java.util.logging.SimpleFormatter;\nimport java.util.logging.StreamHandler;\n\npublic class StandardOutConsoleHandler extends StreamHandler\n{\n    public StandardOutConsoleHandler(Formatter formatter)\n    {\n        super(new FileOutputStream(FileDescriptor.out),\n                Objects.requireNonNull(formatter, "formatter"));\n    }\n\n    public StandardOutConsoleHandler() { this(new SimpleFormatter()); }\n\n    /* Taken from java.logging/java.util.logging.ConsoleHandler. */\n    @Override\n    public void publish(LogRecord record)\n    {\n        super.publish(record);\n        flush();\n    }\n\n    /* Taken from java.logging/java.util.logging.ConsoleHandler. */\n    @Override\n    public void close() { flush(); }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

过滤器(可选)。

\n\n
package org.foo.logging.internal;\n\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.time.temporal.TemporalAccessor;\nimport java.time.temporal.TemporalQuery;\nimport java.util.Objects;\n\nimport java.util.logging.Filter;\nimport java.util.logging.LogRecord;\n\npublic class WallClockTimeFilter implements Filter\n{\n    private static final TemporalQuery<Boolean> BUSINESS_HOURS\n                                            = new BusinessHours();\n\n    static class BusinessHours implements TemporalQuery<Boolean>\n    {\n        private static final LocalTime FROM = LocalTime.of(9, 0);\n        private static final LocalTime TO = LocalTime.of(17, 0);\n\n        @Override\n        public Boolean queryFrom(TemporalAccessor temporal)\n        {\n            final LocalTime now = LocalTime.from(temporal);\n            return (now.isAfter(FROM) && now.isBefore(TO));\n        }\n    }\n\n    @Override\n    public boolean isLoggable(LogRecord record)\n    {\n        Objects.requireNonNull(record, "record");\n        final LocalTime now = LocalTime.ofInstant(record.getInstant(),\n                                                ZoneId.systemDefault());\n        return now.query(BUSINESS_HOURS);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

属性配置器。

\n\n
package org.foo.logging.internal;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport java.util.logging.LogManager;\n\n/*\n * This class could be referenced on the command-line as follows\n *\n * -Djava.util.logging.config.class=org.foo.logging.internal.LoggingPropertiesConfigurer\n *\n * See java.logging/java.util.logging.LogManager#readConfiguration().\n */\npublic class LoggingPropertiesConfigurer\n{\n    private static final String RESOURCE = "/logging.properties";\n\n    public LoggingPropertiesConfigurer() throws IOException\n    {\n        try (final InputStream is = getClass().getResourceAsStream(\n                                                        RESOURCE)) {\n            if (is == null)\n                throw new IllegalStateException(\n                        String.format("Unavailable resource: \'%s\'",\n                                                        RESOURCE));\n\n            /* Prefer new non-null values over old values. */\n            LogManager.getLogManager().updateConfiguration(is,\n                                                    property ->\n                                ((oldValue, newValue) -> {\n                return (oldValue == null && newValue == null)\n                    ? null  /* Discard the property. */\n                    : (newValue == null)\n                        ? oldValue\n                        : newValue;\n            }));\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

一个假人。

\n\n
package org.foo.logging;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport java.util.logging.Logger;\n\nimport org.foo.logging.internal.LoggingPropertiesConfigurer;\n\npublic class Dummy\n{\n    static {\n        try {\n            final String fileName = System.getProperty(\n                            "java.util.logging.config.file");\n            final String klassName = System.getProperty(\n                            "java.util.logging.config.class");\n\n            if (klassName == null && fileName == null)\n                new LoggingPropertiesConfigurer();\n        } catch (final IOException e) {\n            throw new ExceptionInInitializerError(e);\n        }\n    }\n\n    static Optional<Logger> getLogger()\n    {\n        /*\n         * Note that for any org.foo.Bar.Baz.Quux member class\n         * Class::getName returns an org.foo.Bar$Baz$Quux string,\n         * therefore name accordingly these loggers, if any, in\n         * the properties files, e.g.\n         *      org.foo.Bar$Baz$Quux.level = WARNING\n         */\n        return Optional.ofNullable(Logger.getLogger(\n                                        Dummy.class.getName()));\n    }\n\n    public static void main(String[] args)\n    {\n        /*\n         * A weakly-reachable logger.\n         *\n         * See java.base/java.lang.ref.Reference#reachabilityFence(Object)\n         */\n        Dummy.getLogger().ifPresent(logger -> logger.warning(() ->\n                                        Arrays.toString(args)));\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

将模块声明编写为src/org.foo/classes/module-info.java.

\n\n
module org.foo {\n    requires transitive java.logging;\n\n    exports org.foo.logging;\n\n    exports org.foo.logging.internal to\n        java.logging;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

编译类:

\n\n
javac -Xlint -d build/modules --module-source-path src/\\*/classes/ $(find src/*/classes/ -type f -name \\*.java)\n
Run Code Online (Sandbox Code Playgroud)\n\n

描述一个模块:

\n\n
java --describe-module org.foo --module-path build/modules\n
Run Code Online (Sandbox Code Playgroud)\n\n

将属性文件编写为src/org.foo/resources/logging.properties.

\n\n
## From [java.home]/conf/logging.properties:\n# handlers = java.util.logging.ConsoleHandler\n\nhandlers = org.foo.logging.internal.StandardOutConsoleHandler\n\njava.util.logging.SimpleFormatter.format = %1$tY-%<tm-%<td %<tH:%<tM:%<tS %4$s %2$s %5$s%6$s%n\n\n## See the Javadoc of java.logging/java.util.logging.StreamHandler.\norg.foo.logging.internal.StandardOutConsoleHandler.level = ALL\n# org.foo.logging.internal.StandardOutConsoleHandler.filter = org.foo.logging.internal.WallClockTimeFilter\norg.foo.logging.internal.StandardOutConsoleHandler.formatter = java.util.logging.SimpleFormatter\norg.foo.logging.internal.StandardOutConsoleHandler.encoding = ISO-8859-1\n
Run Code Online (Sandbox Code Playgroud)\n\n

复制一份用于包装:

\n\n
cp -t build/modules/org.foo src/org.foo/resources/logging.properties\n
Run Code Online (Sandbox Code Playgroud)\n\n

打包类和复制的资源(观察末尾的. ):

\n\n
jar --create --module-version 0.0.1 --file dist/logger-0.0.1.jar --main-class org.foo.logging.Dummy -C build/modules/org.foo/ .\n
Run Code Online (Sandbox Code Playgroud)\n\n

尝试使用重定向运行stdout, stderr。\n

\nStdout记录。当当地时间允许时,取消注释中与时间相关的过滤器行logging.properties

\n\n
java -Xdiag --module-path dist/logger-0.0.1.jar --module org.foo raison d\\\'\xc3\xaatre 2>/dev/null\n
Run Code Online (Sandbox Code Playgroud)\n\n

Stderr记录。将/path/to/jdk替换为 Java 安装目录的真实路径:

\n\n
java -enablesystemassertions -Xdiag -Djava.util.logging.config.file=/path/to/jdk/conf/logging.properties --module-path dist/logger-0.0.1.jar --module org.foo raison d\\\'\xc3\xaatre 2>/dev/null\n
Run Code Online (Sandbox Code Playgroud)\n