java - 如何通过属性配置 java.util.logging 以使用标准输出?
问题描述
如何通过属性配置 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
解决方案
从<logger>.handlers属性派生一个新的处理程序java.util.logging.StreamHandler
并将其完全限定名称。
(如果使用 Apache Maven,请注意使用 surefire 插件stdout
作为IPC及其分叉子代的一种方式,因此在测试期间会收到损坏警告,请参阅此处。)
例如(Java 9+)...
布局目录:
mkdir -p /tmp/logger/{src/org.foo/{{classes,tests}/org/foo/logging{,/internal},resources},{build/modules/org.foo,dist}}
验证布局:
cd /tmp/logger && gio tree --hidden
文件:///tmp/记录器 |-- 构建 | `-- 模块 | `-- org.foo |-- 分布 `--src `-- org.foo |-- 班级 | `--组织 | `-- 富 | `-- 记录 | `--内部 |-- 资源 `-- 测试 `--组织 `-- 富 `-- 记录 `--内部
src/org.foo/classes
在分支下编写类。
一个处理程序。
package org.foo.logging.internal;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.util.Objects;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
public class StandardOutConsoleHandler extends StreamHandler
{
public StandardOutConsoleHandler(Formatter formatter)
{
super(new FileOutputStream(FileDescriptor.out),
Objects.requireNonNull(formatter, "formatter"));
}
public StandardOutConsoleHandler() { this(new SimpleFormatter()); }
/* Taken from java.logging/java.util.logging.ConsoleHandler. */
@Override
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
/* Taken from java.logging/java.util.logging.ConsoleHandler. */
@Override
public void close() { flush(); }
}
过滤器(可选)。
package org.foo.logging.internal;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.Objects;
import java.util.logging.Filter;
import java.util.logging.LogRecord;
public class WallClockTimeFilter implements Filter
{
private static final TemporalQuery<Boolean> BUSINESS_HOURS
= new BusinessHours();
static class BusinessHours implements TemporalQuery<Boolean>
{
private static final LocalTime FROM = LocalTime.of(9, 0);
private static final LocalTime TO = LocalTime.of(17, 0);
@Override
public Boolean queryFrom(TemporalAccessor temporal)
{
final LocalTime now = LocalTime.from(temporal);
return (now.isAfter(FROM) && now.isBefore(TO));
}
}
@Override
public boolean isLoggable(LogRecord record)
{
Objects.requireNonNull(record, "record");
final LocalTime now = LocalTime.ofInstant(record.getInstant(),
ZoneId.systemDefault());
return now.query(BUSINESS_HOURS);
}
}
属性配置器。
package org.foo.logging.internal;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
/*
* This class could be referenced on the command-line as follows
*
* -Djava.util.logging.config.class=org.foo.logging.internal.LoggingPropertiesConfigurer
*
* See java.logging/java.util.logging.LogManager#readConfiguration().
*/
public class LoggingPropertiesConfigurer
{
private static final String RESOURCE = "/logging.properties";
public LoggingPropertiesConfigurer() throws IOException
{
try (final InputStream is = getClass().getResourceAsStream(
RESOURCE)) {
if (is == null)
throw new IllegalStateException(
String.format("Unavailable resource: '%s'",
RESOURCE));
/* Prefer new non-null values over old values. */
LogManager.getLogManager().updateConfiguration(is,
property ->
((oldValue, newValue) -> {
return (oldValue == null && newValue == null)
? null /* Discard the property. */
: (newValue == null)
? oldValue
: newValue;
}));
}
}
}
一个假人。
package org.foo.logging;
import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;
import java.util.logging.Logger;
import org.foo.logging.internal.LoggingPropertiesConfigurer;
public class Dummy
{
static {
try {
final String fileName = System.getProperty(
"java.util.logging.config.file");
final String klassName = System.getProperty(
"java.util.logging.config.class");
if (klassName == null && fileName == null)
new LoggingPropertiesConfigurer();
} catch (final IOException e) {
throw new ExceptionInInitializerError(e);
}
}
static Optional<Logger> getLogger()
{
/*
* Note that for any org.foo.Bar.Baz.Quux member class
* Class::getName returns an org.foo.Bar$Baz$Quux string,
* therefore name accordingly these loggers, if any, in
* the properties files, e.g.
* org.foo.Bar$Baz$Quux.level = WARNING
*/
return Optional.ofNullable(Logger.getLogger(
Dummy.class.getName()));
}
public static void main(String[] args)
{
/*
* A weakly-reachable logger.
*
* See java.base/java.lang.ref.Reference#reachabilityFence(Object)
*/
Dummy.getLogger().ifPresent(logger -> logger.warning(() ->
Arrays.toString(args)));
}
}
将模块声明编写为src/org.foo/classes/module-info.java
.
module org.foo {
requires transitive java.logging;
exports org.foo.logging;
exports org.foo.logging.internal to
java.logging;
}
编译类:
javac -Xlint -d build/modules --module-source-path src/\*/classes/ $(find src/*/classes/ -type f -name \*.java)
描述一个模块:
java --describe-module org.foo --module-path build/modules
将属性文件写为src/org.foo/resources/logging.properties
.
## From [java.home]/conf/logging.properties:
# handlers = java.util.logging.ConsoleHandler
handlers = org.foo.logging.internal.StandardOutConsoleHandler
java.util.logging.SimpleFormatter.format = %1$tY-%<tm-%<td %<tH:%<tM:%<tS %4$s %2$s %5$s%6$s%n
## See the Javadoc of java.logging/java.util.logging.StreamHandler.
org.foo.logging.internal.StandardOutConsoleHandler.level = ALL
# org.foo.logging.internal.StandardOutConsoleHandler.filter = org.foo.logging.internal.WallClockTimeFilter
org.foo.logging.internal.StandardOutConsoleHandler.formatter = java.util.logging.SimpleFormatter
org.foo.logging.internal.StandardOutConsoleHandler.encoding = ISO-8859-1
复制一份用于包装:
cp -t build/modules/org.foo src/org.foo/resources/logging.properties
打包类和复制的资源(注意最后的.):
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/ .
尝试使用重定向stdout
的 ,运行stderr
。
Stdout
记录。当本地时间允许时,取消注释中与时间相关的过滤器行logging.properties
:
java -Xdiag --module-path dist/logger-0.0.1.jar --module org.foo raison d\'être 2>/dev/null
Stderr
记录。将 Java 安装目录的真实路径替换为/path/to/jdk:
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\'être 2>/dev/null
推荐阅读
- android - 如何复用(合并)视频和音频,以便音频在输出视频中循环播放,以防持续时间太短?
- php - PHP图像上传在本地主机上工作,但在服务器上不工作
- javascript - 谷歌图表创建相对 URL
- visual-studio-code - 用于语言扩展的 VSCode IntelliSense 关键字
- python - 将过程数据保存在数据库中的另一个表上
- python - Boost - 在 Python 中导入 Qt 创建的 C++ Python 模块
- asp.net-mvc - 如何在 Azure DevOps 发布管道中构建不同的配置?
- python - Python按长度切割句子
- mysql - 无法将小数 (4,2) 转换为小数 (10,2)
- android - Android 应用发布到 Play 商店然后添加 Firebase 通知崩溃