gpt4 book ai didi

使用带有追加的 getHead() 的 Java 日志记录

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:16:42 25 4
gpt4 key购买 nike

我们正在开发一个网络应用程序,我正在服务器端实现事件日志记录。我决定一个不错的方法是扩展 Java 日志记录 API 以将事件记录到 CSV 文件中。我创建了一个 java.util.logging.Formatter 类,如下所示:

public class EventLogCsvFormatter extends Formatter {
private static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy h:mm a");
private static final String[] COL_HEADERS = {"Date/Time", "Source", "Event Type", "Application Context", "Description", "Fields"};

public EventLogCsvFormatter() {
}

@Override
public String getHead(Handler h) {
return String.join(",", COL_HEADERS);
}

@Override
public String format(LogRecord record) {
if (record instanceof EventLogRecord) {
EventLogRecord customLogRecord = (EventLogRecord) record;
String[] fields = customLogRecord.getFields();
List<String> textList = new ArrayList<>();

textList.add(SDF.format(new Date(record.getMillis())));
textList.add(customLogRecord.getSource() != null ? customLogRecord.getSource() : "Not Given");
textList.add(customLogRecord.getEventType() != null ? customLogRecord.getEventType().toString() : "Not Given");
textList.add(customLogRecord.getEventContext() != null ? customLogRecord.getEventContext().toString() : "Not Given");
textList.add(customLogRecord.getMessage());

if (fields != null && fields.length > 0) {
for (String field : fields) {
textList.add(field);
}
}

String retVal = "\n" + String.join(",", textList);
return retVal;
}

return "";
}

实现 getHead() 方法的想法是为每个 CSV 文件提供列标题。请注意,Java 自己的 XMLFormatter 在其 getHead() 方法中返回 XML 文件头字符串时做了类似的事情。事实证明,Java 日志记录在调用 getHead() 时不会考虑追加标志。附加标志基本上告诉记录器重新打开现有文件并在启动时继续记录到它。因为我们的测试服务器经常被反弹,所以生成的大多数 CSV 文件在文件的多个位置都有列标题名称,而不仅仅是在顶部。

我认为没有任何解决办法,因为几乎所有 Java 日志记录处理程序代码都有私有(private)或包范围的字段和方法。所以,我什至无法编写自己的自定义处理程序来在这里工作。这是一个错误,我是 SOL?我是否应该继续使用不同的日志记录 API(例如 Log4J)?

最佳答案

与此问题相关的 FileHandler RFE 是 JDK-4629315: Appending of XML Logfiles doesn't merge new recordsJDK-5036335: Provide method to obtain log file name(s) and path(s) .

完成这项工作的诀窍是,当请求 header 时,您必须能够查询当前的日志文件。如果当前日志文件长度为零,则格式化程序必须返回 header 。

在您的自定义格式化程序中,您可以让 getHead 方法尝试定位打开的文件并使用 java.iojava.nio 查询长度。

@Override
public String getHead(Handler h) {
boolean writeHeader = true;
try {
if (h instanceof FileHandler) {
writeHeader = lengthOpen((FileHandler) h).longValue() == 0L;
}
} catch (SecurityException ignore) {
}

if (writeHeader) {
return ""; //TODO: Insert your CSV headers.
} else {
return super.getHead(h); //Skip headers.
}
}

private Number lengthOpen(Handler h) {
if (h instanceof FileHandler) {
String p = h.getClass().getName();
LogManager manager = LogManager.getLogManager();
p = manager.getProperty(p.concat(".pattern"));
//TODO: Deal with FileHandler patterns.
if (p != null) {
File f = new File(p);
//TODO: Implement file listing and filtering.
return f.length();
}
}
return 0L;
}

否则,如果您想进行一些黑客攻击,您可以求助于反射。

@Override
public String getHead(Handler h) {
boolean writeHeader = true;
try {
if (h instanceof FileHandler) {
writeHeader = lengthFrom((FileHandler) h).longValue() == 0L;
}
} catch (SecurityException ignore) {
}

if (writeHeader) {
return ""; //TODO: Insert your CSV headers here.
} else {
return super.getHead(h); //Skip headers.
}
}

private Number lengthFrom(FileHandler h) {
try {
Field f = StreamHandler.class.getDeclaredField("output");
f.setAccessible(true);
OutputStream out = (OutputStream) f.get(h);
f = out.getClass().getDeclaredField("written");
f.setAccessible(true);
return (Number) f.get(out);
} catch (ReflectiveOperationException roe) {
h.getErrorManager().error(null, roe, ErrorManager.FORMAT_FAILURE);
}
return 0L;
}

关于使用带有追加的 getHead() 的 Java 日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34359166/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com